true

Step 0: check and install needed packages. Load the libraries and functions.

library("sentimentr")
Warning message:
running command '"C:/Program Files/RStudio/bin/pandoc/pandoc" +RTS -K512m -RTS wk2-Tutorial-TextMining.utf8.md --to html --from markdown+autolink_bare_uris+ascii_identifiers+tex_math_single_backslash --output pandoc2b144ebbb5.html --smart --email-obfuscation none --self-contained --standalone --section-divs --template "C:\Users\JL\Documents\R\win-library\3.3\rmarkdown\rmd\h\default.html" --no-highlight --variable highlightjs=1 --variable "theme:bootstrap" --include-in-header "C:\Users\JL\AppData\Local\Temp\RtmpMFdQjt\rmarkdown-str2b141eae42de.html" --mathjax --variable "mathjax-url:https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML" --variable code_folding=show --variable source_embed=wk2-Tutorial-TextMining.Rmd --include-after-body "C:\Users\JL\AppData\Local\Temp\RtmpMFdQjt\file2b1456c31d17.html" --variable code_menu=1 --variable kable-scroll=1' had status 67 

This notebook was prepared with the following environmental settings.

print(R.version)

Step 1: Data harvest: scrap speech URLs from http://www.presidency.ucsb.edu/.

Following the example of Jerid Francom, we used Selectorgadget to choose the links we would like to scrap. For this project, we selected all inaugural addresses of past presidents, nomination speeches of major party candidates and farewell addresses. We also included several public speeches from Donald Trump for our textual analysis of presidential speeches.

### Inauguaral speeches
main.page <- read_html(x = "http://www.presidency.ucsb.edu/inaugurals.php")
# Get link URLs
# f.speechlinks is a function for extracting links from the list of speeches. 
inaug=f.speechlinks(main.page)
#head(inaug)
as.Date(inaug[,1], format="%B %e, %Y")
 [1] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[30] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[59] NA
inaug=inaug[-nrow(inaug),] # remove the last line, irrelevant due to error.
#### Nomination speeches
main.page=read_html("http://www.presidency.ucsb.edu/nomination.php")
# Get link URLs
nomin <- f.speechlinks(main.page)
#head(nomin)
#
#### Farewell speeches
main.page=read_html("http://www.presidency.ucsb.edu/farewell_addresses.php")
# Get link URLs
farewell <- f.speechlinks(main.page)
#head(farewell)

Step 2: Using speech metadata posted on http://www.presidency.ucsb.edu/, we prepared CSV data sets for the speeches we will scrap.

inaug.list=read.csv("../data/inauglist.csv", stringsAsFactors = FALSE)
nomin.list=read.csv("../data/nominlist.csv", stringsAsFactors = FALSE)
farewell.list=read.csv("../data/farewelllist.csv", stringsAsFactors = FALSE)

We assemble all scrapped speeches into one list. Note here that we don’t have the full text yet, only the links to full text transcripts.

Step 3: scrap the texts of speeches from the speech URLs.

speech.list=rbind(inaug.list, nomin.list, farewell.list)
speech.list$type=c(rep("inaug", nrow(inaug.list)),
                   rep("nomin", nrow(nomin.list)),
                   rep("farewell", nrow(farewell.list)))
speech.url=rbind(inaug, nomin, farewell)
speech.list=cbind(speech.list, speech.url)

Based on the list of speeches, we scrap the main text part of the transcript’s html page. For simple html pages of this kind, Selectorgadget is very convenient for identifying the html node that rvest can use to scrap its content. For reproducibility, we also save our scrapped speeches into our local folder as individual speech files.

# Loop over each row in speech.list
speech.list$fulltext=NA
for(i in seq(nrow(speech.list))) {
  text <- read_html(speech.list$urls[i]) %>% # load the page
    html_nodes(".displaytext") %>% # isloate the text
    html_text() # get the text
  speech.list$fulltext[i]=text
  # Create the file name
  filename <- paste0("../data/fulltext/", 
                     speech.list$type[i],
                     speech.list$File[i], "-", 
                     speech.list$Term[i], ".txt")
  sink(file = filename) %>% # open file to write 
  cat(text)  # write the file
  sink() # close the file
}

Trump, as president-elect that has not been a politician, do not have a lot of formal speeches yet. For our textual analysis, we manually add several public transcripts from Trump: + [Transcript: Donald Trump’s full immigration speech, annotated. LA Times, 08/31/2016] (http://www.latimes.com/politics/la-na-pol-donald-trump-immigration-speech-transcript-20160831-snap-htmlstory.html) + Transcript of Donald Trump鈥檚 speech on national security in Philadelphia - The Hill, 09/07/16 + Transcript of President-elect Trump’s news conference CNBC, 01/11/2017

Step 4: data Processing — generate list of sentences

We will use sentences as units of analysis for this project, as sentences are natural languge units for organizing thoughts and ideas. For each extracted sentence, we apply sentiment analysis using NRC sentiment lexion. “The NRC Emotion Lexicon is a list of English words and their associations with eight basic emotions (anger, fear, anticipation, trust, surprise, sadness, joy, and disgust) and two sentiments (negative and positive). The annotations were manually done by crowdsourcing.”

We assign an sequential id to each sentence in a speech (sent.id) and also calculated the number of words in each sentence as sentence length (word.count).

sentence.list=NULL
for(i in 1:nrow(speech.list)){
  sentences=sent_detect(speech.list$fulltext[i],
                        endmarks = c("?", ".", "!", "|",";"))
  if(length(sentences)>0){
    emotions=get_nrc_sentiment(sentences)
    word.count=word_count(sentences)
    # colnames(emotions)=paste0("emo.", colnames(emotions))
    # in case the word counts are zeros?
    emotions=diag(1/(word.count+0.01))%*%as.matrix(emotions)
    sentence.list=rbind(sentence.list, 
                        cbind(speech.list[i,-ncol(speech.list)],
                              sentences=as.character(sentences), 
                              word.count,
                              emotions,
                              sent.id=1:length(sentences)
                              )
    )
  }
}

Some non-sentences exist in raw data due to erroneous extra end-of sentence marks.

sentence.list=
  sentence.list%>%
  filter(!is.na(word.count)) 

Step 5: Data analysis — length of sentences

For simpler visualization, we chose a subset of better known presidents or presidential candidates on which to focus our analysis.

sel.comparison=c("DonaldJTrump","JohnMcCain", "GeorgeBush", "MittRomney", "GeorgeWBush",
                 "RonaldReagan","AlbertGore,Jr", "HillaryClinton","JohnFKerry", 
                 "WilliamJClinton","HarrySTruman", "BarackObama", "LyndonBJohnson",
                 "GeraldRFord", "JimmyCarter", "DwightDEisenhower", "FranklinDRoosevelt",
                 "HerbertHoover","JohnFKennedy","RichardNixon","WoodrowWilson", 
                 "AbrahamLincoln", "TheodoreRoosevelt", "JamesGarfield", 
                 "JohnQuincyAdams", "UlyssesSGrant", "ThomasJefferson",
                 "GeorgeWashington", "WilliamHowardTaft", "AndrewJackson",
                 "WilliamHenryHarrison", "JohnAdams")

Overview of sentence length distribution by different types of speeches.

Nomination speeches

First, we look at nomination acceptance speeches at major party’s national conventions. For relevant to Trump’s speeches, we limit our attention to speeches for the first terms of former U.S. presidents. We noticed that a number of presidents have very short sentences in their nomination acceptance speeches.

First term

par(mar=c(2, 5, 1, 1))
#sel.comparison=levels(sentence.list$FileOrdered)
sentence.list.sel=filter(sentence.list, 
                        type=="nomin", Term==1, File%in%sel.comparison)
sentence.list.sel$File=factor(sentence.list.sel$File)
sentence.list.sel$FileOrdered=reorder(sentence.list.sel$File, 
                                  sentence.list.sel$word.count, 
                                  mean, 
                                  order=T)
beeswarm(word.count~FileOrdered, 
         data=sentence.list.sel,
         horizontal = TRUE, 
         pch=16, col=alpha(brewer.pal(9, "Set1"), 0.6), 
         cex=0.55, cex.axis=0.8, cex.lab=0.8,
         spacing=5/nlevels(sentence.list.sel$FileOrdered),
         las=2, xlab="Number of words in a sentence.", ylab="",
         main="Nomination speeches")

Second term

par(mar=c(2, 5, 1, 1))
#sel.comparison=levels(sentence.list$FileOrdered)
sentence.list.sel=filter(sentence.list, 
                        type=="nomin", Term==2, File%in%sel.comparison)
sentence.list.sel$File=factor(sentence.list.sel$File)
sentence.list.sel$FileOrdered=reorder(sentence.list.sel$File, 
                                  sentence.list.sel$word.count, 
                                  mean, 
                                  order=T)
beeswarm(word.count~FileOrdered, 
         data=sentence.list.sel,
         horizontal = TRUE, 
         pch=16, col=alpha(brewer.pal(9, "Set1"), 0.6), 
         cex=0.55, cex.axis=0.8, cex.lab=0.8,
         spacing=1.2/nlevels(sentence.list.sel$FileOrdered),
         las=2, xlab="Number of words in a sentence.", ylab="",
         main="Nomination speeches, 2nd term")

What are these short sentences?

sentence.list%>%
  filter(File=="DonaldJTrump", 
         type=="nomin", 
         word.count<=3)%>%
  select(sentences)%>%sample_n(10)
sentence.list%>%
  filter(File=="AlbertGore,Jr", 
         type=="nomin", 
         word.count<=3)%>%
  select(sentences)%>%sample_n(10)
sentence.list%>%
  filter(File=="Clinton", 
         type=="nomin", 
         word.count<=3)%>%
  select(sentences)
sentence.list%>%
  filter(File=="WilliamJClinton", 
         type=="nomin", Term==1,
         word.count<=3)%>%
  select(sentences)

Inaugural speeches

We notice that the sentences in inaugural speeches are longer than those in nomination acceptance speeches.

sentence.list.sel=sentence.list%>%filter(type=="inaug", File%in%sel.comparison, Term==1)
sentence.list.sel$File=factor(sentence.list.sel$File)
sentence.list.sel$FileOrdered=reorder(sentence.list.sel$File, 
                                  sentence.list.sel$word.count, 
                                  mean, 
                                  order=T)
par(mar=c(2, 5, 1, 1))
beeswarm(word.count~FileOrdered, 
         data=sentence.list.sel,
         horizontal = TRUE,
         pch=16, col=alpha(brewer.pal(9, "Set1"), 0.6), 
         cex=0.55, cex.axis=0.8, cex.lab=0.8,
         spacing=5/nlevels(sentence.list.sel$FileOrdered),
         las=2, ylab="", xlab="Number of words in a sentence.",
         main="Inaugural Speeches")

Short sentences in inaugural speeches.

sentence.list%>%
  filter(File=="BarackObama", 
         type=="inaug", 
         word.count<=3)%>%
  select(sentences)

Step 5: Data analysis — sentiment analsis

Sentence length variation over the course of the speech, with emotions.

How our presidents (or candidates) alternate between long and short sentences and how they shift between different sentiments in their speeches. It is interesting to note that some presidential candidates’ speech are more colorful than others. Here we used the same color theme as in the movie “Inside Out.”

image

image

par(mfrow=c(4,1), mar=c(0,0,2,0), bty="n", xaxt="n", yaxt="n", font.main=1)
f.plotsent.len(In.list=sentence.list, InFile="HillaryClinton", 
               InType="nomin", InTerm=1, President="Hillary Clinton")
f.plotsent.len(In.list=sentence.list, InFile="DonaldJTrump", 
               InType="nomin", InTerm=1, President="Donald Trump")
f.plotsent.len(In.list=sentence.list, InFile="BarackObama", 
               InType="nomin", InTerm=1, President="Barack Obama")
f.plotsent.len(In.list=sentence.list, InFile="GeorgeWBush", 
               InType="nomin", InTerm=1, President="George W. Bush")

What are the emotionally charged sentences?

print("Hillary Clinton")
[1] "Hillary Clinton"
speech.df=tbl_df(sentence.list)%>%
  filter(File=="HillaryClinton", type=="nomin", word.count>=4)%>%
  select(sentences, anger:trust)
speech.df=as.data.frame(speech.df)
as.character(speech.df$sentences[apply(speech.df[,-1], 2, which.max)])
[1] "Some of you are frustrated, even furious."                        
[2] "It's a big deal."                                                 
[3] "Powerful forces are threatening to pull us apart."                
[4] "Powerful forces are threatening to pull us apart."                
[5] "It's a big deal."                                                 
[6] "My mother, Dorothy, was abandoned by her parents as a young girl."
[7] "It's a big deal."                                                 
[8] "Bonds of trust and respect are fraying."                          
print("Barack Obama")
[1] "Barack Obama"
speech.df=tbl_df(sentence.list)%>%
  filter(File=="BarackObama", type=="nomin", Term==1, word.count>=5)%>%
  select(sentences, anger:trust)
speech.df=as.data.frame(speech.df)
as.character(speech.df$sentences[apply(speech.df[,-1], 2, which.max)])
[1] "They could've heard words of anger and discord."
[2] "And that's to be expected."                     
[3] "It's not because John McCain doesn't care."     
[4] "Now let there be no doubt."                     
[5] "That promise is our greatest inheritance."      
[6] "Now let there be no doubt."                     
[7] "That's not the judgment we need."               
[8] "That promise is our greatest inheritance."      
print("George W Bush")
[1] "George W Bush"
speech.df=tbl_df(sentence.list)%>%
  filter(File=="GeorgeWBush", type=="nomin", Term==1, word.count>=4)%>%
  select(sentences, anger:trust)
speech.df=as.data.frame(speech.df)
as.character(speech.df$sentences[apply(speech.df[,-1], 2, which.max)])
[1] "On the other side of that wall are poverty and prison, addiction and despair."                                           
[2] "We're proud of you."                                                                                                     
[3] "But they've got it backwards."                                                                                           
[4] "And at the earliest possible date, my administration will deploy missile defenses to guard against attack and blackmail."
[5] "I appreciate his friendship."                                                                                            
[6] "On the other side of that wall are poverty and prison, addiction and despair."                                           
[7] "They had their chance."                                                                                                  
[8] "Corporations are responsible to treat their workers fairly and to leave the air and waters clean."                       
print("Donald Trump")
[1] "Donald Trump"
speech.df=tbl_df(sentence.list)%>%
  filter(File=="DonaldJTrump", type=="nomin", Term==1, word.count>=5)%>%
  select(sentences, anger:trust)
speech.df=as.data.frame(speech.df)
as.character(speech.df$sentences[apply(speech.df[,-1], 2, which.max)])
[1] "Once again, France is the victim of brutal Islamic terrorism."                                                   
[2] "God bless you, and good night!"                                                                                  
[3] "I have visited the laid-off factory workers, and the communities crushed by our horrible and unfair trade deals."
[4] "Once again, France is the victim of brutal Islamic terrorism."                                                   
[5] "God bless you, and good night!"                                                                                  
[6] "Three were killed, and three were very very badly injured."                                                      
[7] "God bless you, and good night!"                                                                                  
[8] "God bless you, and good night!"                                                                                  

Clustering of emotions

heatmap.2(cor(sentence.list%>%filter(type=="inaug")%>%select(anger:trust)), 
          scale = "none", 
          col = bluered(100), , margin=c(6, 6), key=F,
          trace = "none", density.info = "none")
par(mar=c(4, 6, 2, 1))

emo.means=colMeans(select(sentence.list, anger:trust)>0.01)
col.use=c("red2", "darkgoldenrod1", 
            "chartreuse3", "blueviolet",
            "darkgoldenrod2", "dodgerblue3", 
            "darkgoldenrod1", "darkgoldenrod1")
barplot(emo.means[order(emo.means)], las=2, col=col.use[order(emo.means)], horiz=T, main="Inaugural Speeches")

presid.summary=tbl_df(sentence.list)%>%
  filter(type=="nomin", File%in%sel.comparison)%>%
  #group_by(paste0(type, File))%>%
  group_by(File)%>%
  summarise(
    anger=mean(anger),
    anticipation=mean(anticipation),
    disgust=mean(disgust),
    fear=mean(fear),
    joy=mean(joy),
    sadness=mean(sadness),
    surprise=mean(surprise),
    trust=mean(trust)
    #negative=mean(negative),
    #positive=mean(positive)
  )
presid.summary=as.data.frame(presid.summary)
rownames(presid.summary)=as.character((presid.summary[,1]))
km.res=kmeans(presid.summary[,-1], iter.max=200,
              5)
fviz_cluster(km.res, 
             stand=F, repel= TRUE,
             data = presid.summary[,-1], xlab="", xaxt="n",
             show.clust.cent=FALSE)

Step 5: Data analysis — Topic modeling

For topic modeling, we prepare a corpus of sentence snipets as follows. For each speech, we start with sentences and prepare a snipet with a given sentence with the flanking sentences.

corpus.list=sentence.list[2:(nrow(sentence.list)-1), ]
sentence.pre=sentence.list$sentences[1:(nrow(sentence.list)-2)]
sentence.post=sentence.list$sentences[3:(nrow(sentence.list)-1)]
corpus.list$snipets=paste(sentence.pre, corpus.list$sentences, sentence.post, sep=" ")
rm.rows=(1:nrow(corpus.list))[corpus.list$sent.id==1]
rm.rows=c(rm.rows, rm.rows-1)
corpus.list=corpus.list[-rm.rows, ]

Text mining

docs <- Corpus(VectorSource(corpus.list$snipets))
writeLines(as.character(docs[[sample(1:nrow(corpus.list), 1)]]))
We propose to rebuild the key tools of missile defense, starting with the Navy cruisers that are the foundation of our missile defense capabilities in Europe, Asia, and the Middle East. The Obama-Clinton administration tried repeatedly to remove our cruisers from service, then refused to modernize these aging ships. We will start by modernizing our cruisers to provide the Ballistic Missile Defense capability our nation needs;

Text basic processing

Adapted from https://eight2late.wordpress.com/2015/09/29/a-gentle-introduction-to-topic-modeling-using-r/.

#remove potentially problematic symbols
docs <-tm_map(docs,content_transformer(tolower))
writeLines(as.character(docs[[sample(1:nrow(corpus.list), 1)]]))
now, folks, you cheered—and i thank you— but the government can only do so much. the private sector has to provide most of these jobs. so i want to say again, tonight i challenge every business person in america who has ever complained about the failure of the welfare system to try to hire somebody off welfare and try hard.
#remove punctuation
docs <- tm_map(docs, removePunctuation)
writeLines(as.character(docs[[sample(1:nrow(corpus.list), 1)]]))
and by putting the federal department of labor at the disposal of the workingman when in search of work we have effected the emancipation of the children of the country by releasing them from hurtful labor we have instituted a system of national aid in the building of highroads such as the country has been feeling after for a century
#Strip digits
docs <- tm_map(docs, removeNumbers)
writeLines(as.character(docs[[sample(1:nrow(corpus.list), 1)]]))
the people must once more unite and overcome the advocates of repudiation and must not relax their energy until the battle for public honor and honest money shall again triumph a congress which will sustain and if need be strengthen the present law can prevent a financial catastrophe which every lover of the republic is interested to avert not satisfied with assaulting the currency and credit of the government our political adversaries condemn the tariff enacted at the extra session of congress in  known as the dingley act passed in obedience to the will of the people expressed at the election in the preceding november a law which at once stimulated our industries opened the factories and mines and gave to the laborer and to the farmers fair returns for their toil and investment
#remove stopwords
docs <- tm_map(docs, removeWords, stopwords("english"))
writeLines(as.character(docs[[sample(1:nrow(corpus.list), 1)]]))
  transaction   foreign affairs   endeavored  cultivate  friendship   nations  especially         important relations   done  justice   occasions favored  favor  lawful  cherished mutual interests  intercourse  fair  equal terms   firmly convinced   act   conviction   nations   individuals  interests soundly calculated will ever  found inseparable   moral duties  history bears witness   fact   just nation  trusted   word  recourse    armaments  wars  bridle others
#remove whitespace
docs <- tm_map(docs, stripWhitespace)
writeLines(as.character(docs[[sample(1:nrow(corpus.list), 1)]]))
 well known blessings enjoyed fullest extent add peculiar satisfaction example capital punishment inflicted anyone crime high treason might admit competency government beneficent duties might doubt trials put test strength efficiency member great community nations
#Stem document
docs <- tm_map(docs,stemDocument)
writeLines(as.character(docs[[sample(1:nrow(corpus.list), 1)]]))
now turn issu overrid import elect generat cometh progress made build new structur peac world peac import partisanship five presid polit lifetimefranklin d

Topic modeling

Gengerate document-term matrices.

dtm <- DocumentTermMatrix(docs)
#convert rownames to filenames#convert rownames to filenames
rownames(dtm) <- paste(corpus.list$type, corpus.list$File,
                       corpus.list$Term, corpus.list$sent.id, sep="_")
rowTotals <- apply(dtm , 1, sum) #Find the sum of words in each Document
Reached total allocation of 3792Mb: see help(memory.size)Reached total allocation of 3792Mb: see help(memory.size)Reached total allocation of 3792Mb: see help(memory.size)Reached total allocation of 3792Mb: see help(memory.size)Reached total allocation of 3792Mb: see help(memory.size)Reached total allocation of 3792Mb: see help(memory.size)Reached total allocation of 3792Mb: see help(memory.size)Reached total allocation of 3792Mb: see help(memory.size)Reached total allocation of 3792Mb: see help(memory.size)Reached total allocation of 3792Mb: see help(memory.size)Reached total allocation of 3792Mb: see help(memory.size)Reached total allocation of 3792Mb: see help(memory.size)Reached total allocation of 3792Mb: see help(memory.size)Reached total allocation of 3792Mb: see help(memory.size)Reached total allocation of 3792Mb: see help(memory.size)Reached total allocation of 3792Mb: see help(memory.size)Reached total allocation of 3792Mb: see help(memory.size)Reached total allocation of 3792Mb: see help(memory.size)Reached total allocation of 3792Mb: see help(memory.size)Reached total allocation of 3792Mb: see help(memory.size)
dtm  <- dtm[rowTotals> 0, ]
corpus.list=corpus.list[rowTotals>0, ]

Run LDA

#Set parameters for Gibbs sampling
burnin <- 4000
iter <- 2000
thin <- 500
seed <-list(2003,5,63,100001,765)
nstart <- 5
best <- TRUE
#Number of topics
k <- 15
#Run LDA using Gibbs sampling
ldaOut <-LDA(dtm, k, method="Gibbs", control=list(nstart=nstart, 
                                                 seed = seed, best=best,
                                                 burnin = burnin, iter = iter, 
                                                 thin=thin))
Reached total allocation of 3792Mb: see help(memory.size)Reached total allocation of 3792Mb: see help(memory.size)Error in get(helpTopicsName, pos = rsEnvPos) : 
  object '.completions.helpTopics' not found
help(memory.size)
#write out results
#docs to topics
ldaOut.topics <- as.matrix(topics(ldaOut))
table(c(1:k, ldaOut.topics))

   1    2    3    4    5    6    7    8    9   10   11   12   13   14   15 
1507 1580 2486  953 1287  717  979 1649 1746 1492 1300 1688 1281 1182 1133 
write.csv(ldaOut.topics,file=paste("../out/LDAGibbs",k,"DocsToTopics.csv"))
#top 6 terms in each topic
ldaOut.terms <- as.matrix(terms(ldaOut,20))
write.csv(ldaOut.terms,file=paste("../out/LDAGibbs",k,"TopicsToTerms.csv"))
#probabilities associated with each topic assignment
topicProbabilities <- as.data.frame(ldaOut@gamma)
write.csv(topicProbabilities,file=paste("../out/LDAGibbs",k,"TopicProbabilities.csv"))
terms.beta=ldaOut@beta
terms.beta=scale(terms.beta)
topics.terms=NULL
for(i in 1:k){
  topics.terms=rbind(topics.terms, ldaOut@terms[order(terms.beta[i,], decreasing = TRUE)[1:7]])
}
topics.terms
      [,1]            [,2]       [,3]         [,4]         [,5]       [,6]       
 [1,] "fellowcitizen" "discharg" "enlighten"  "bestow"     "exert"    "zeal"     
 [2,] "aspir"         "surrend"  "decenc"     "concept"    "invent"   "yearn"    
 [3,] "dont"          "immigr"   "didnt"      "polic"      "russia"   "someon"   
 [4,] "deed"          "fist"     "dissatisfi" "leak"       "matthew"  "pessimist"
 [5,] "time"          "sinc"     "three"      "almost"     "took"     "six"      
 [6,] "lake"          "awak"     "evenhand"   "mar"        "rancor"   "dimens"   
 [7,] "understood"    "frustrat" "optimist"   "enthusiasm" "grim"     "nobil"    
 [8,] "war"           "forc"     "defens"     "europ"      "east"     "soviet"   
 [9,] "convent"       "campaign" "john"       "governor"   "reagan"   "speech"   
[10,] "currenc"       "commerci" "expans"     "cent"       "export"   "european" 
[11,] "bridg"         "unpreced" "edg"        "pioneer"    "beacon"   "bread"    
[12,] "job"           "educ"     "billion"    "technolog"  "elder"    "senior"   
[13,] "voic"          "journey"  "brave"      "gift"       "daughter" "boy"      
[14,] "sovereignti"   "tend"     "tendenc"    "sphere"     "confin"   "jealous"  
[15,] "law"           "amend"    "appoint"    "judg"       "disput"   "railroad" 
      [,7]        
 [1,] "emin"      
 [2,] "wrought"   
 [3,] "shes"      
 [4,] "americaan" 
 [5,] "ceremoni"  
 [6,] "secreci"   
 [7,] "fantasi"   
 [8,] "negoti"    
 [9,] "eisenhow"  
[10,] "government"
[11,] "noblest"   
[12,] "bigger"    
[13,] "bright"    
[14,] "usurp"     
[15,] "interst"   
ldaOut.terms
      Topic 1    Topic 2     Topic 3  Topic 4      Topic 5     Topic 6   Topic 7  
 [1,] "may"      "right"     "want"   "will"       "year"      "countri" "can"    
 [2,] "shall"    "nation"    "know"   "must"       "time"      "one"     "peopl"  
 [3,] "public"   "freedom"   "just"   "everi"      "now"       "great"   "make"   
 [4,] "duti"     "peopl"     "like"   "never"      "first"     "peopl"   "believ" 
 [5,] "upon"     "free"      "think"  "nation"     "made"      "mani"    "good"   
 [6,] "confid"   "human"     "back"   "respons"    "last"      "citizen" "chang"  
 [7,] "trust"    "equal"     "dont"   "make"       "histori"   "much"    "way"    
 [8,] "servic"   "liberti"   "get"    "now"        "come"      "part"    "thing"  
 [9,] "spirit"   "justic"    "that"   "continu"    "ago"       "place"   "need"   
[10,] "honor"    "civil"     "tell"   "lead"       "long"      "differ"  "problem"
[11,] "best"     "life"      "ive"    "whether"    "past"      "anoth"   "done"   
[12,] "patriot"  "democraci" "fight"  "need"       "two"       "polit"   "valu"   
[13,] "feel"     "progress"  "say"    "take"       "sinc"      "serv"    "find"   
[14,] "high"     "among"     "immigr" "support"    "still"     "person"  "life"   
[15,] "present"  "seek"      "someth" "give"       "end"       "fact"    "work"   
[16,] "experi"   "individu"  "talk"   "effort"     "administr" "ever"    "learn"  
[17,] "purpos"   "men"       "put"    "understand" "four"      "import"  "choic"  
[18,] "oblig"    "hold"      "even"   "task"       "record"    "call"    "truth"  
[19,] "principl" "full"      "happen" "requir"     "today"     "other"   "help"   
[20,] "interest" "republ"    "weve"   "bring"      "day"       "alway"   "well"   
      Topic 8    Topic 9      Topic 10   Topic 11   Topic 12   Topic 13 Topic 14   
 [1,] "nation"   "presid"     "busi"     "america"  "work"     "hope"   "govern"   
 [2,] "peac"     "parti"      "system"   "american" "famili"   "see"    "state"    
 [3,] "world"    "republican" "industri" "new"      "job"      "men"    "power"    
 [4,] "war"      "say"        "upon"     "let"      "tax"      "man"    "unit"     
 [5,] "forc"     "democrat"   "product"  "world"    "children" "day"    "union"    
 [6,] "polici"   "ask"        "increas"  "futur"    "million"  "love"   "constitut"
 [7,] "defens"   "elect"      "labor"    "live"     "help"     "stand"  "interest" 
 [8,] "strength" "thank"      "trade"    "better"   "care"     "god"    "principl" 
 [9,] "danger"   "tonight"    "public"   "promis"   "economi"  "live"   "general"  
[10,] "militari" "american"   "develop"  "togeth"   "pay"      "women"  "form"     
[11,] "use"      "friend"     "polici"   "build"    "school"   "young"  "limit"    
[12,] "arm"      "accept"     "use"      "opportun" "program"  "heart"  "within"   
[13,] "also"     "offic"      "tariff"   "home"     "educ"     "know"   "exist"    
[14,] "unit"     "convent"    "import"   "today"    "plan"     "land"   "upon"     
[15,] "strong"   "proud"      "protect"  "generat"  "creat"    "faith"  "action"   
[16,] "intern"   "said"       "revenu"   "centuri"  "home"     "across" "feder"    
[17,] "clear"    "leader"     "market"   "old"      "save"     "rememb" "institut" 
[18,] "maintain" "member"     "employ"   "challeng" "health"   "call"   "whole"    
[19,] "secur"    "hous"       "price"    "come"     "reduc"    "side"   "citizen"  
[20,] "europ"    "unit"       "farmer"   "begin"    "worker"   "fear"   "local"    
      Topic 15   
 [1,] "law"      
 [2,] "congress" 
 [3,] "question" 
 [4,] "shall"    
 [5,] "without"  
 [6,] "administr"
 [7,] "secur"    
 [8,] "execut"   
 [9,] "legisl"   
[10,] "constitut"
[11,] "made"     
[12,] "protect"  
[13,] "subject"  
[14,] "order"    
[15,] "enforc"   
[16,] "effect"   
[17,] "upon"     
[18,] "may"      
[19,] "act"      
[20,] "issu"     

Based on the most popular terms and the most salient terms for each topic, we assign a hashtag to each topic. This part require manual setup as the topics are likely to change.

topics.hash=c("Economy", "America", "Defense", "Belief", "Election", "Patriotism", "Unity", "Government", "Reform", "Temporal", "WorkingFamilies", "Freedom", "Equality", "Misc", "Legislation")
corpus.list$ldatopic=as.vector(ldaOut.topics)
corpus.list$ldahash=topics.hash[ldaOut.topics]
colnames(topicProbabilities)=topics.hash
corpus.list.df=cbind(corpus.list, topicProbabilities)

Clustering of topics

par(mar=c(1,1,1,1))
topic.summary=tbl_df(corpus.list.df)%>%
              filter(type%in%c("nomin", "inaug"), File%in%sel.comparison)%>%
              select(File, Economy:Legislation)%>%
              group_by(File)%>%
              summarise_each(funs(mean))
topic.summary=as.data.frame(topic.summary)
rownames(topic.summary)=topic.summary[,1]
# [1] "Economy"         "America"         "Defense"         "Belief"         
# [5] "Election"        "Patriotism"      "Unity"           "Government"     
# [9] "Reform"          "Temporal"        "WorkingFamilies" "Freedom"        
# [13] "Equality"        "Misc"            "Legislation"       
topic.plot=c(1, 13, 9, 11, 8, 3, 7)
print(topics.hash[topic.plot])
[1] "Economy"         "Equality"        "Reform"          "WorkingFamilies"
[5] "Government"      "Defense"         "Unity"          
heatmap.2(as.matrix(topic.summary[,topic.plot+1]), 
          scale = "column", key=F, 
          col = bluered(100),
          cexRow = 0.9, cexCol = 0.9, margins = c(8, 8),
          trace = "none", density.info = "none")

# [1] "Economy"         "America"         "Defense"         "Belief"         
# [5] "Election"        "Patriotism"      "Unity"           "Government"     
# [9] "Reform"          "Temporal"        "WorkingFamilies" "Freedom"        
# [13] "Equality"        "Misc"            "Legislation"       
 
par(mfrow=c(5, 1), mar=c(1,1,2,0), bty="n", xaxt="n", yaxt="n")
topic.plot=c(1, 13, 14, 15, 8, 9, 12)
print(topics.hash[topic.plot])
[1] "Economy"     "Equality"    "Misc"        "Legislation" "Government"  "Reform"     
[7] "Freedom"    
speech.df=tbl_df(corpus.list.df)%>%filter(File=="GeorgeBush", type=="nomin",Term==1)%>%select(sent.id, Economy:Legislation)
speech.df=as.matrix(speech.df)
speech.df[,-1]=replace(speech.df[,-1], speech.df[,-1]<1/15, 0.001)
speech.df[,-1]=f.smooth.topic(x=speech.df[,1], y=speech.df[,-1])
plot.stacked(speech.df[,1], speech.df[,topic.plot+1], 
             xlab="Sentences", ylab="Topic share", main="George Bush, Nomination")
[1] 0.05345318 0.10690636 0.16035954 0.21381272 0.26726590 0.32071908 0.37417225
speech.df=tbl_df(corpus.list.df)%>%filter(File=="WilliamJClinton", type=="nomin", Term==1)%>%select(sent.id, Economy:Legislation)
speech.df=as.matrix(speech.df)
speech.df[,-1]=replace(speech.df[,-1], speech.df[,-1]<1/15, 0.001)
speech.df[,-1]=f.smooth.topic(x=speech.df[,1], y=speech.df[,-1])
plot.stacked(speech.df[,1], speech.df[,topic.plot+1],
            xlab="Sentences", ylab="Topic share", main="Bill Clinton, Nomination")
[1] 0.05477343 0.10954687 0.16432030 0.21909373 0.27386716 0.32864060 0.38341403
speech.df=tbl_df(corpus.list.df)%>%filter(File=="GeorgeWBush", type=="nomin", Term==1)%>%select(sent.id, Economy:Legislation)
speech.df=as.matrix(speech.df)
speech.df[,-1]=replace(speech.df[,-1], speech.df[,-1]<1/15, 0.001)
speech.df[,-1]=f.smooth.topic(x=speech.df[,1], y=speech.df[,-1])
plot.stacked(speech.df[,1], speech.df[,topic.plot+1], 
            xlab="Sentences", ylab="Topic share", main="George W Bush, Nomination")
[1] 0.05298909 0.10597818 0.15896728 0.21195637 0.26494546 0.31793455 0.37092364
speech.df=tbl_df(corpus.list.df)%>%filter(File=="BarackObama", type=="nomin", Term==1)%>%select(sent.id, Economy:Legislation)
speech.df=as.matrix(speech.df)
speech.df[,-1]=replace(speech.df[,-1], speech.df[,-1]<1/15, 0.001)
speech.df[,-1]=f.smooth.topic(x=speech.df[,1], y=speech.df[,-1])
plot.stacked(speech.df[,1], speech.df[,topic.plot+1],
            xlab="Sentences", ylab="Topic share", main="Barack Obama, Nomination")
[1] 0.05220546 0.10441092 0.15661638 0.20882184 0.26102731 0.31323277 0.36543823
speech.df=tbl_df(corpus.list.df)%>%filter(File=="DonaldJTrump", type=="nomin")%>%select(sent.id, Economy:Legislation)
speech.df=as.matrix(speech.df)
speech.df[,-1]=replace(speech.df[,-1], speech.df[,-1]<1/15, 0.001)
speech.df[,-1]=f.smooth.topic(x=speech.df[,1], y=speech.df[,-1])
plot.stacked(speech.df[,1], speech.df[,topic.plot+1],
            xlab="Sentences", ylab="Topic share", main="Donald Trump, Nomination")
[1] 0.04597271 0.09194541 0.13791812 0.18389083 0.22986353 0.27583624 0.32180895

# [1] "Economy"         "America"         "Defense"         "Belief"         
# [5] "Election"        "Patriotism"      "Unity"           "Government"     
# [9] "Reform"          "Temporal"        "WorkingFamilies" "Freedom"        
# [13] "Equality"        "Misc"            "Legislation"       
par(mfrow=c(5, 1), mar=c(1,1,2,0), bty="n", xaxt="n", yaxt="n")
topic.plot=c(1, 13, 14, 15, 8, 9, 12)
print(topics.hash[topic.plot])
[1] "Economy"     "Equality"    "Misc"        "Legislation" "Government"  "Reform"     
[7] "Freedom"    
speech.df=tbl_df(corpus.list.df)%>%filter(File=="GeorgeBush", type=="inaug", Term==1)%>%select(sent.id, Economy:Legislation)
speech.df=as.matrix(speech.df)
speech.df[,-1]=replace(speech.df[,-1], speech.df[,-1]<1/15, 0.001)
speech.df[,-1]=f.smooth.topic(x=speech.df[,1], y=speech.df[,-1])
plot.stacked(speech.df[,1], speech.df[,topic.plot+1],
             xlab="Sentences", ylab="Topic share", main="George Bush, inaugural Speeches")
[1] 0.05515922 0.11031843 0.16547765 0.22063687 0.27579609 0.33095530 0.38611452
speech.df=tbl_df(corpus.list.df)%>%filter(File=="WilliamJClinton", type=="inaug", Term==1)%>%select(sent.id, Economy:Legislation)
speech.df=as.matrix(speech.df)
speech.df[,-1]=replace(speech.df[,-1], speech.df[,-1]<1/15, 0.001)
speech.df[,-1]=f.smooth.topic(x=speech.df[,1], y=speech.df[,-1])
plot.stacked(speech.df[,1], speech.df[,topic.plot+1],
             xlab="Sentences", ylab="Topic share", main="William J Clinton, inaugural Speeches")
[1] 0.04966348 0.09932696 0.14899044 0.19865392 0.24831740 0.29798088 0.34764436
speech.df=tbl_df(corpus.list.df)%>%filter(File=="GeorgeWBush", type=="inaug", Term==1)%>%select(sent.id, Economy:Legislation)
speech.df=as.matrix(speech.df)
speech.df[,-1]=replace(speech.df[,-1], speech.df[,-1]<1/15, 0.001)
speech.df[,-1]=f.smooth.topic(x=speech.df[,1], y=speech.df[,-1])
plot.stacked(speech.df[,1], speech.df[,topic.plot+1],
             xlab="Sentences", ylab="Topic share", main="George W. Bush, inaugural Speeches")
[1] 0.05479929 0.10959859 0.16439788 0.21919718 0.27399647 0.32879576 0.38359506
speech.df=tbl_df(corpus.list.df)%>%filter(File=="BarackObama", type=="inaug", Term==1)%>%select(sent.id, Economy:Legislation)
speech.df=as.matrix(speech.df)
speech.df[,-1]=replace(speech.df[,-1], speech.df[,-1]<1/15, 0.001)
speech.df[,-1]=f.smooth.topic(x=speech.df[,1], y=speech.df[,-1])
plot.stacked(speech.df[,1], speech.df[,topic.plot+1],
             xlab="Sentences", ylab="Topic share", main="Barack Obama, inaugural Speeches")
[1] 0.04853656 0.09707311 0.14560967 0.19414622 0.24268278 0.29121933 0.33975589

# [1] "Economy"         "America"         "Defense"         "Belief"         
# [5] "Election"        "Patriotism"      "Unity"           "Government"     
# [9] "Reform"          "Temporal"        "WorkingFamilies" "Freedom"        
# [13] "Equality"        "Misc"            "Legislation"       
par(mfrow=c(5, 1))
topic.plot=c(1, 13, 14, 15, 8, 9, 12)
print(topics.hash[topic.plot])
[1] "Economy"     "Equality"    "Misc"        "Legislation" "Government"  "Reform"     
[7] "Freedom"    
speech.df=tbl_df(corpus.list.df)%>%filter(File=="RonaldReagan", type=="farewell")%>%select(sent.id, Economy:Legislation)
speech.df=as.matrix(speech.df)
speech.df[,-1]=replace(speech.df[,-1], speech.df[,-1]<1/15, 0.001)
speech.df[,-1]=f.smooth.topic(x=speech.df[,1], y=speech.df[,-1])
plot.stacked(speech.df[,1], speech.df[,topic.plot+1],
             xlab="Sentences", ylab="Topic share", main="Ronald Reagan, Farewell Speeches")
[1] 0.04998233 0.09996466 0.14994699 0.19992932 0.24991166 0.29989399 0.34987632
speech.df=tbl_df(corpus.list.df)%>%filter(File=="GeorgeBush", type=="farewell")%>%select(sent.id, Economy:Legislation)
speech.df=as.matrix(speech.df)
speech.df[,-1]=replace(speech.df[,-1], speech.df[,-1]<1/15, 0.001)
speech.df[,-1]=f.smooth.topic(x=speech.df[,1], y=speech.df[,-1])
plot.stacked(speech.df[,1], speech.df[,topic.plot+1],
             xlab="Sentences", ylab="Topic share", main="George Bush, Farewell Speeches")
[1] 0.05151229 0.10302458 0.15453687 0.20604916 0.25756145 0.30907374 0.36058603
speech.df=tbl_df(corpus.list.df)%>%filter(File=="WilliamJClinton", type=="farewell")%>%select(sent.id, Economy:Legislation)
Warning: Unknown column 'sentences'
Warning: Unknown column 'sentences'
Warning: Unknown column 'sentences'
Warning: Unknown column 'sentences'
Warning: Unknown column 'sentences'
Warning: Unknown column 'sentences'
Warning: Unknown column 'sentences'
Warning: Unknown column 'sentences'
Warning: Unknown column 'sentences'
Warning: Unknown column 'sentences'
speech.df=as.matrix(speech.df)
speech.df[,-1]=replace(speech.df[,-1], speech.df[,-1]<1/15, 0.001)
speech.df[,-1]=f.smooth.topic(x=speech.df[,1], y=speech.df[,-1])
plot.stacked(speech.df[,1], speech.df[,topic.plot+1],
             xlab="Sentences", ylab="Topic share", main="William J. Clinton, Farewell Speeches")
[1] 0.04670749 0.09341499 0.14012248 0.18682997 0.23353746 0.28024496 0.32695245
speech.df=tbl_df(corpus.list.df)%>%filter(File=="GeorgeWBush", type=="farewell")%>%select(sent.id, Economy:Legislation)
speech.df=as.matrix(speech.df)
speech.df[,-1]=replace(speech.df[,-1], speech.df[,-1]<1/15, 0.001)
speech.df[,-1]=f.smooth.topic(x=speech.df[,1], y=speech.df[,-1])
plot.stacked(speech.df[,1], speech.df[,topic.plot+1],
             xlab="Sentences", ylab="Topic share", main="George W Bush, Farewell Speeches")
[1] 0.05571697 0.11143394 0.16715091 0.22286788 0.27858484 0.33430181 0.39001878
speech.df=tbl_df(corpus.list.df)%>%filter(File=="BarackObama", type=="farewell")%>%select(sent.id, Economy:Legislation)
speech.df=as.matrix(speech.df)
speech.df[,-1]=replace(speech.df[,-1], speech.df[,-1]<1/15, 0.001)
speech.df[,-1]=f.smooth.topic(x=speech.df[,1], y=speech.df[,-1])
plot.stacked(speech.df[,1], speech.df[,topic.plot+1],
             xlab="Sentences", ylab="Topic share", main="Barack Obama, Farewell Speeches")
[1] 0.04720441 0.09440882 0.14161323 0.18881764 0.23602205 0.28322647 0.33043088

speech.df=tbl_df(corpus.list.df)%>%filter(type=="nomin", word.count<20)%>%select(sentences, Economy:Legislation)
as.character(speech.df$sentences[apply(as.data.frame(speech.df[,-1]), 2, which.max)])
 [1] "With great respect, I am Very truly yours, J."                                                             
 [2] "It has been wrought through party government."                                                             
 [3] "But not a single hunter in Arkansas or New Hampshire or Illinois or anyplace else missed a hunting season."
 [4] "It will be decided by you."                                                                                
 [5] "Well, I should—pardon me—I didn't put a period in there."                                                  
 [6] "They share the same piece of land, but they are different from one another."                               
 [7] "My friends, as President, I'm going to be setting goals for our country;"                                  
 [8] "We have helped to bring democracy to Haiti and peace to Bosnia."                                           
 [9] "Mr."                                                                                                       
[10] "Of great importance and still in a highly undeveloped state are our transportation routes by waterways."   
[11] "Let us resolve to build that bridge."                                                                      
[12] "My tax cuts will not undermine our economy, they will speed economic growth."                              
[13] "Crowds spontaneously began singing America the Beautiful or The Battle Hymn of the Republic."              
[14] "Its enforcement requires, therefore, independent but coincident action of both agencies."                  
[15] "With respect to notice, the Democratic platform contains no recommendation."                               
names(speech.df)[-1]
 [1] "Economy"         "America"         "Defense"         "Belief"         
 [5] "Election"        "Patriotism"      "Unity"           "Government"     
 [9] "Reform"          "Temporal"        "WorkingFamilies" "Freedom"        
[13] "Equality"        "Misc"            "Legislation"    
presid.summary=tbl_df(corpus.list.df)%>%
  filter(type=="inaug", File%in%sel.comparison)%>%
  select(File, Economy:Legislation)%>%
  group_by(File)%>%
  summarise_each(funs(mean))
presid.summary=as.data.frame(presid.summary)
rownames(presid.summary)=as.character((presid.summary[,1]))
km.res=kmeans(scale(presid.summary[,-1]), iter.max=200,
              5)
fviz_cluster(km.res, 
             stand=T, repel= TRUE,
             data = presid.summary[,-1],
             show.clust.cent=FALSE)

LS0tDQp0aXRsZTogJ1R1dG9yaWFsICh3ZWVrIDIpIEI6IHRleHQgbWluaW5nJw0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOiBkZWZhdWx0DQogIGh0bWxfZG9jdW1lbnQ6IGRlZmF1bHQNCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQp0b2M6IHllcw0KdG9jX2RlcHRoOiAyDQotLS0NCg0KIyBTdGVwIDA6IGNoZWNrIGFuZCBpbnN0YWxsIG5lZWRlZCBwYWNrYWdlcy4gTG9hZCB0aGUgbGlicmFyaWVzIGFuZCBmdW5jdGlvbnMuIA0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnBhY2thZ2VzLnVzZWQ9YygicnZlc3QiLCAidGliYmxlIiwgInFkYXAiLCANCiAgICAgICAgICAgICAgICAic2VudGltZW50ciIsICJncGxvdHMiLCAiZHBseXIiLA0KICAgICAgICAgICAgICAgICJ0bSIsICJzeXV6aGV0IiwgImZhY3RvZXh0cmEiLCANCiAgICAgICAgICAgICAgICAiYmVlc3dhcm0iLCAic2NhbGVzIiwgIlJDb2xvckJyZXdlciIsDQogICAgICAgICAgICAgICAgIlJBTk4iLCAidG0iLCAidG9waWNtb2RlbHMiKQ0KDQojIGNoZWNrIHBhY2thZ2VzIHRoYXQgbmVlZCB0byBiZSBpbnN0YWxsZWQuDQpwYWNrYWdlcy5uZWVkZWQ9c2V0ZGlmZihwYWNrYWdlcy51c2VkLCANCiAgICAgICAgICAgICAgICAgICAgICAgIGludGVyc2VjdChpbnN0YWxsZWQucGFja2FnZXMoKVssMV0sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhY2thZ2VzLnVzZWQpKQ0KIyBpbnN0YWxsIGFkZGl0aW9uYWwgcGFja2FnZXMNCmlmKGxlbmd0aChwYWNrYWdlcy5uZWVkZWQpPjApew0KICBpbnN0YWxsLnBhY2thZ2VzKHBhY2thZ2VzLm5lZWRlZCwgZGVwZW5kZW5jaWVzID0gVFJVRSkNCn0NCg0KIyBsb2FkIHBhY2thZ2VzDQpsaWJyYXJ5KCJydmVzdCIpDQpsaWJyYXJ5KCJ0aWJibGUiKQ0KbGlicmFyeSgicWRhcCIpDQpsaWJyYXJ5KCJzZW50aW1lbnRyIikNCmxpYnJhcnkoImdwbG90cyIpDQpsaWJyYXJ5KCJkcGx5ciIpDQpsaWJyYXJ5KCJ0bSIpDQpsaWJyYXJ5KCJzeXV6aGV0IikNCmxpYnJhcnkoImZhY3RvZXh0cmEiKQ0KbGlicmFyeSgiYmVlc3dhcm0iKQ0KbGlicmFyeSgic2NhbGVzIikNCmxpYnJhcnkoIlJDb2xvckJyZXdlciIpDQpsaWJyYXJ5KCJSQU5OIikNCmxpYnJhcnkoInRtIikNCmxpYnJhcnkoInRvcGljbW9kZWxzIikNCg0Kc291cmNlKCIuLi9saWIvcGxvdHN0YWNrZWQuUiIpDQpzb3VyY2UoIi4uL2xpYi9zcGVlY2hGdW5jcy5SIikNCmBgYA0KVGhpcyBub3RlYm9vayB3YXMgcHJlcGFyZWQgd2l0aCB0aGUgZm9sbG93aW5nIGVudmlyb25tZW50YWwgc2V0dGluZ3MuDQoNCmBgYHtyfQ0KcHJpbnQoUi52ZXJzaW9uKQ0KYGBgDQoNCiMgU3RlcCAxOiBEYXRhIGhhcnZlc3Q6IHNjcmFwIHNwZWVjaCBVUkxzIGZyb20gPGh0dHA6Ly93d3cucHJlc2lkZW5jeS51Y3NiLmVkdS8+Lg0KDQpGb2xsb3dpbmcgdGhlIGV4YW1wbGUgb2YgW0plcmlkIEZyYW5jb21dKGh0dHA6Ly9mcmFuY29qYy5naXRodWIuaW8vd2ViLXNjcmFwaW5nLXdpdGgtcnZlc3QvKSwgd2UgdXNlZCBbU2VsZWN0b3JnYWRnZXRdKGh0dHA6Ly9zZWxlY3RvcmdhZGdldC5jb20vKSB0byBjaG9vc2UgdGhlIGxpbmtzIHdlIHdvdWxkIGxpa2UgdG8gc2NyYXAuIEZvciB0aGlzIHByb2plY3QsIHdlIHNlbGVjdGVkIGFsbCBpbmF1Z3VyYWwgYWRkcmVzc2VzIG9mIHBhc3QgcHJlc2lkZW50cywgbm9taW5hdGlvbiBzcGVlY2hlcyBvZiBtYWpvciBwYXJ0eSBjYW5kaWRhdGVzIGFuZCBmYXJld2VsbCBhZGRyZXNzZXMuIFdlIGFsc28gaW5jbHVkZWQgc2V2ZXJhbCBwdWJsaWMgc3BlZWNoZXMgZnJvbSBEb25hbGQgVHJ1bXAgZm9yIG91ciB0ZXh0dWFsIGFuYWx5c2lzIG9mIHByZXNpZGVudGlhbCBzcGVlY2hlcy4gDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyMjIEluYXVndWFyYWwgc3BlZWNoZXMNCm1haW4ucGFnZSA8LSByZWFkX2h0bWwoeCA9ICJodHRwOi8vd3d3LnByZXNpZGVuY3kudWNzYi5lZHUvaW5hdWd1cmFscy5waHAiKQ0KIyBHZXQgbGluayBVUkxzDQojIGYuc3BlZWNobGlua3MgaXMgYSBmdW5jdGlvbiBmb3IgZXh0cmFjdGluZyBsaW5rcyBmcm9tIHRoZSBsaXN0IG9mIHNwZWVjaGVzLiANCmluYXVnPWYuc3BlZWNobGlua3MobWFpbi5wYWdlKQ0KI2hlYWQoaW5hdWcpDQphcy5EYXRlKGluYXVnWywxXSwgZm9ybWF0PSIlQiAlZSwgJVkiKQ0KaW5hdWc9aW5hdWdbLW5yb3coaW5hdWcpLF0gIyByZW1vdmUgdGhlIGxhc3QgbGluZSwgaXJyZWxldmFudCBkdWUgdG8gZXJyb3IuDQoNCiMjIyMgTm9taW5hdGlvbiBzcGVlY2hlcw0KbWFpbi5wYWdlPXJlYWRfaHRtbCgiaHR0cDovL3d3dy5wcmVzaWRlbmN5LnVjc2IuZWR1L25vbWluYXRpb24ucGhwIikNCiMgR2V0IGxpbmsgVVJMcw0Kbm9taW4gPC0gZi5zcGVlY2hsaW5rcyhtYWluLnBhZ2UpDQojaGVhZChub21pbikNCiMNCiMjIyMgRmFyZXdlbGwgc3BlZWNoZXMNCm1haW4ucGFnZT1yZWFkX2h0bWwoImh0dHA6Ly93d3cucHJlc2lkZW5jeS51Y3NiLmVkdS9mYXJld2VsbF9hZGRyZXNzZXMucGhwIikNCiMgR2V0IGxpbmsgVVJMcw0KZmFyZXdlbGwgPC0gZi5zcGVlY2hsaW5rcyhtYWluLnBhZ2UpDQojaGVhZChmYXJld2VsbCkNCmBgYA0KDQojIFN0ZXAgMjogVXNpbmcgc3BlZWNoIG1ldGFkYXRhIHBvc3RlZCBvbiA8aHR0cDovL3d3dy5wcmVzaWRlbmN5LnVjc2IuZWR1Lz4sIHdlIHByZXBhcmVkIENTViBkYXRhIHNldHMgZm9yIHRoZSBzcGVlY2hlcyB3ZSB3aWxsIHNjcmFwLiANCg0KYGBge3J9DQppbmF1Zy5saXN0PXJlYWQuY3N2KCIuLi9kYXRhL2luYXVnbGlzdC5jc3YiLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpDQpub21pbi5saXN0PXJlYWQuY3N2KCIuLi9kYXRhL25vbWlubGlzdC5jc3YiLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpDQpmYXJld2VsbC5saXN0PXJlYWQuY3N2KCIuLi9kYXRhL2ZhcmV3ZWxsbGlzdC5jc3YiLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpDQpgYGANCg0KV2UgYXNzZW1ibGUgYWxsIHNjcmFwcGVkIHNwZWVjaGVzIGludG8gb25lIGxpc3QuIE5vdGUgaGVyZSB0aGF0IHdlIGRvbid0IGhhdmUgdGhlIGZ1bGwgdGV4dCB5ZXQsIG9ubHkgdGhlIGxpbmtzIHRvIGZ1bGwgdGV4dCB0cmFuc2NyaXB0cy4gDQoNCiMgU3RlcCAzOiBzY3JhcCB0aGUgdGV4dHMgb2Ygc3BlZWNoZXMgZnJvbSB0aGUgc3BlZWNoIFVSTHMuDQoNCmBgYHtyfQ0Kc3BlZWNoLmxpc3Q9cmJpbmQoaW5hdWcubGlzdCwgbm9taW4ubGlzdCwgZmFyZXdlbGwubGlzdCkNCnNwZWVjaC5saXN0JHR5cGU9YyhyZXAoImluYXVnIiwgbnJvdyhpbmF1Zy5saXN0KSksDQogICAgICAgICAgICAgICAgICAgcmVwKCJub21pbiIsIG5yb3cobm9taW4ubGlzdCkpLA0KICAgICAgICAgICAgICAgICAgIHJlcCgiZmFyZXdlbGwiLCBucm93KGZhcmV3ZWxsLmxpc3QpKSkNCnNwZWVjaC51cmw9cmJpbmQoaW5hdWcsIG5vbWluLCBmYXJld2VsbCkNCnNwZWVjaC5saXN0PWNiaW5kKHNwZWVjaC5saXN0LCBzcGVlY2gudXJsKQ0KYGBgDQoNCkJhc2VkIG9uIHRoZSBsaXN0IG9mIHNwZWVjaGVzLCB3ZSBzY3JhcCB0aGUgbWFpbiB0ZXh0IHBhcnQgb2YgdGhlIHRyYW5zY3JpcHQncyBodG1sIHBhZ2UuIEZvciBzaW1wbGUgaHRtbCBwYWdlcyBvZiB0aGlzIGtpbmQsICBbU2VsZWN0b3JnYWRnZXRdKGh0dHA6Ly9zZWxlY3RvcmdhZGdldC5jb20vKSBpcyB2ZXJ5IGNvbnZlbmllbnQgZm9yIGlkZW50aWZ5aW5nIHRoZSBodG1sIG5vZGUgdGhhdCBgcnZlc3RgIGNhbiB1c2UgdG8gc2NyYXAgaXRzIGNvbnRlbnQuIEZvciByZXByb2R1Y2liaWxpdHksIHdlIGFsc28gc2F2ZSBvdXIgc2NyYXBwZWQgc3BlZWNoZXMgaW50byBvdXIgbG9jYWwgZm9sZGVyIGFzIGluZGl2aWR1YWwgc3BlZWNoIGZpbGVzLiANCg0KYGBge3J9DQojIExvb3Agb3ZlciBlYWNoIHJvdyBpbiBzcGVlY2gubGlzdA0Kc3BlZWNoLmxpc3QkZnVsbHRleHQ9TkENCmZvcihpIGluIHNlcShucm93KHNwZWVjaC5saXN0KSkpIHsNCiAgdGV4dCA8LSByZWFkX2h0bWwoc3BlZWNoLmxpc3QkdXJsc1tpXSkgJT4lICMgbG9hZCB0aGUgcGFnZQ0KICAgIGh0bWxfbm9kZXMoIi5kaXNwbGF5dGV4dCIpICU+JSAjIGlzbG9hdGUgdGhlIHRleHQNCiAgICBodG1sX3RleHQoKSAjIGdldCB0aGUgdGV4dA0KICBzcGVlY2gubGlzdCRmdWxsdGV4dFtpXT10ZXh0DQogICMgQ3JlYXRlIHRoZSBmaWxlIG5hbWUNCiAgZmlsZW5hbWUgPC0gcGFzdGUwKCIuLi9kYXRhL2Z1bGx0ZXh0LyIsIA0KICAgICAgICAgICAgICAgICAgICAgc3BlZWNoLmxpc3QkdHlwZVtpXSwNCiAgICAgICAgICAgICAgICAgICAgIHNwZWVjaC5saXN0JEZpbGVbaV0sICItIiwgDQogICAgICAgICAgICAgICAgICAgICBzcGVlY2gubGlzdCRUZXJtW2ldLCAiLnR4dCIpDQogIHNpbmsoZmlsZSA9IGZpbGVuYW1lKSAlPiUgIyBvcGVuIGZpbGUgdG8gd3JpdGUgDQogIGNhdCh0ZXh0KSAgIyB3cml0ZSB0aGUgZmlsZQ0KICBzaW5rKCkgIyBjbG9zZSB0aGUgZmlsZQ0KfQ0KYGBgDQoNClRydW1wLCBhcyBwcmVzaWRlbnQtZWxlY3QgdGhhdCBoYXMgbm90IGJlZW4gYSBwb2xpdGljaWFuLCBkbyBub3QgaGF2ZSBhIGxvdCBvZiBmb3JtYWwgc3BlZWNoZXMgeWV0LiBGb3Igb3VyIHRleHR1YWwgYW5hbHlzaXMsIHdlIG1hbnVhbGx5IGFkZCBzZXZlcmFsIHB1YmxpYyB0cmFuc2NyaXB0cyBmcm9tIFRydW1wOg0KKyBbVHJhbnNjcmlwdDogRG9uYWxkIFRydW1wJ3MgZnVsbCBpbW1pZ3JhdGlvbiBzcGVlY2gsIGFubm90YXRlZC4gTEEgVGltZXMsIDA4LzMxLzIwMTZdIChodHRwOi8vd3d3LmxhdGltZXMuY29tL3BvbGl0aWNzL2xhLW5hLXBvbC1kb25hbGQtdHJ1bXAtaW1taWdyYXRpb24tc3BlZWNoLXRyYW5zY3JpcHQtMjAxNjA4MzEtc25hcC1odG1sc3RvcnkuaHRtbCkNCisgW1RyYW5zY3JpcHQgb2YgRG9uYWxkIFRydW1w4oCZcyBzcGVlY2ggb24gbmF0aW9uYWwgc2VjdXJpdHkgaW4gUGhpbGFkZWxwaGlhDQotIFRoZSBIaWxsLCAwOS8wNy8xNl0oaHR0cDovL3RoZWhpbGwuY29tL2Jsb2dzL3B1bmRpdHMtYmxvZy9jYW1wYWlnbi8yOTQ4MTctdHJhbnNjcmlwdC1vZi1kb25hbGQtdHJ1bXBzLXNwZWVjaC1vbi1uYXRpb25hbC1zZWN1cml0eS1pbikNCisgW1RyYW5zY3JpcHQgb2YgUHJlc2lkZW50LWVsZWN0IFRydW1wJ3MgbmV3cyBjb25mZXJlbmNlDQpDTkJDLCAwMS8xMS8yMDE3XShodHRwOi8vd3d3LmNuYmMuY29tLzIwMTcvMDEvMTEvdHJhbnNjcmlwdC1vZi1wcmVzaWRlbnQtZWxlY3QtZG9uYWxkLWotdHJ1bXBzLW5ld3MtY29uZmVyZW5jZS5odG1sKQ0KDQpgYGB7cn0NCnNwZWVjaDE9cGFzdGUocmVhZExpbmVzKCIuLi9kYXRhL2Z1bGx0ZXh0L1NwZWVjaERvbmFsZFRydW1wLU5BLnR4dCIsIA0KICAgICAgICAgICAgICAgICAgbj0tMSwgc2tpcE51bD1UUlVFKSwNCiAgICAgICAgICAgICAgY29sbGFwc2U9IiAiKQ0Kc3BlZWNoMj1wYXN0ZShyZWFkTGluZXMoIi4uL2RhdGEvZnVsbHRleHQvU3BlZWNoRG9uYWxkVHJ1bXAtTkEyLnR4dCIsIA0KICAgICAgICAgICAgICAgICAgbj0tMSwgc2tpcE51bD1UUlVFKSwNCiAgICAgICAgICAgICAgY29sbGFwc2U9IiAiKQ0Kc3BlZWNoMz1wYXN0ZShyZWFkTGluZXMoIi4uL2RhdGEvZnVsbHRleHQvUHJlc3NEb25hbGRUcnVtcC1OQS50eHQiLCANCiAgICAgICAgICAgICAgICAgIG49LTEsIHNraXBOdWw9VFJVRSksDQogICAgICAgICAgICAgIGNvbGxhcHNlPSIgIikNCg0KVHJ1bXAuc3BlZWNoZXM9ZGF0YS5mcmFtZSgNCiAgUHJlc2lkZW50PXJlcCgiRG9uYWxkIEouIFRydW1wIiwgMyksDQogIEZpbGU9cmVwKCJEb25hbGRKVHJ1bXAiLCAzKSwNCiAgVGVybT1yZXAoMCwgMyksDQogIFBhcnR5PXJlcCgiUmVwdWJsaWNhbiIsIDMpLA0KICBEYXRlPWMoIkF1Z3VzdCAzMSwgMjAxNiIsICJTZXB0ZW1iZXIgNywgMjAxNiIsICJKYW51YXJ5IDExLCAyMDE3IiksDQogIFdvcmRzPWMod29yZF9jb3VudChzcGVlY2gxKSwgd29yZF9jb3VudChzcGVlY2gyKSwgd29yZF9jb3VudChzcGVlY2gzKSksDQogIFdpbj1yZXAoInllcyIsIDMpLA0KICB0eXBlPXJlcCgic3BlZWNoZXMiLCAzKSwNCiAgbGlua3M9cmVwKE5BLCAzKSwNCiAgdXJscz1yZXAoTkEsIDMpLA0KICBmdWxsdGV4dD1jKHNwZWVjaDEsIHNwZWVjaDIsIHNwZWVjaDMpDQopDQoNCmNvbG5hbWVzKHNwZWVjaC5saXN0KVsxXSA8LSBjKCJQcmVzaWRlbnQiKQ0KDQpzcGVlY2gubGlzdD1yYmluZChzcGVlY2gubGlzdCwgVHJ1bXAuc3BlZWNoZXMpDQpgYGANCg0KIyBTdGVwIDQ6IGRhdGEgUHJvY2Vzc2luZyAtLS0gZ2VuZXJhdGUgbGlzdCBvZiBzZW50ZW5jZXMNCg0KV2Ugd2lsbCB1c2Ugc2VudGVuY2VzIGFzIHVuaXRzIG9mIGFuYWx5c2lzIGZvciB0aGlzIHByb2plY3QsIGFzIHNlbnRlbmNlcyBhcmUgbmF0dXJhbCBsYW5ndWdlIHVuaXRzIGZvciBvcmdhbml6aW5nIHRob3VnaHRzIGFuZCBpZGVhcy4gRm9yIGVhY2ggZXh0cmFjdGVkIHNlbnRlbmNlLCB3ZSBhcHBseSBzZW50aW1lbnQgYW5hbHlzaXMgdXNpbmcgW05SQyBzZW50aW1lbnQgbGV4aW9uXShodHRwOi8vc2FpZm1vaGFtbWFkLmNvbS9XZWJQYWdlcy9OUkMtRW1vdGlvbi1MZXhpY29uLmh0bSkuICJUaGUgTlJDIEVtb3Rpb24gTGV4aWNvbiBpcyBhIGxpc3Qgb2YgRW5nbGlzaCB3b3JkcyBhbmQgdGhlaXIgYXNzb2NpYXRpb25zIHdpdGggZWlnaHQgYmFzaWMgZW1vdGlvbnMgKGFuZ2VyLCBmZWFyLCBhbnRpY2lwYXRpb24sIHRydXN0LCBzdXJwcmlzZSwgc2FkbmVzcywgam95LCBhbmQgZGlzZ3VzdCkgYW5kIHR3byBzZW50aW1lbnRzIChuZWdhdGl2ZSBhbmQgcG9zaXRpdmUpLiBUaGUgYW5ub3RhdGlvbnMgd2VyZSBtYW51YWxseSBkb25lIGJ5IGNyb3dkc291cmNpbmcuIg0KDQpXZSBhc3NpZ24gYW4gc2VxdWVudGlhbCBpZCB0byBlYWNoIHNlbnRlbmNlIGluIGEgc3BlZWNoIChgc2VudC5pZGApIGFuZCBhbHNvIGNhbGN1bGF0ZWQgdGhlIG51bWJlciBvZiB3b3JkcyBpbiBlYWNoIHNlbnRlbmNlIGFzICpzZW50ZW5jZSBsZW5ndGgqIChgd29yZC5jb3VudGApLg0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnNlbnRlbmNlLmxpc3Q9TlVMTA0KZm9yKGkgaW4gMTpucm93KHNwZWVjaC5saXN0KSl7DQogIHNlbnRlbmNlcz1zZW50X2RldGVjdChzcGVlY2gubGlzdCRmdWxsdGV4dFtpXSwNCiAgICAgICAgICAgICAgICAgICAgICAgIGVuZG1hcmtzID0gYygiPyIsICIuIiwgIiEiLCAifCIsIjsiKSkNCiAgaWYobGVuZ3RoKHNlbnRlbmNlcyk+MCl7DQogICAgZW1vdGlvbnM9Z2V0X25yY19zZW50aW1lbnQoc2VudGVuY2VzKQ0KICAgIHdvcmQuY291bnQ9d29yZF9jb3VudChzZW50ZW5jZXMpDQogICAgIyBjb2xuYW1lcyhlbW90aW9ucyk9cGFzdGUwKCJlbW8uIiwgY29sbmFtZXMoZW1vdGlvbnMpKQ0KICAgICMgaW4gY2FzZSB0aGUgd29yZCBjb3VudHMgYXJlIHplcm9zPw0KICAgIGVtb3Rpb25zPWRpYWcoMS8od29yZC5jb3VudCswLjAxKSklKiVhcy5tYXRyaXgoZW1vdGlvbnMpDQogICAgc2VudGVuY2UubGlzdD1yYmluZChzZW50ZW5jZS5saXN0LCANCiAgICAgICAgICAgICAgICAgICAgICAgIGNiaW5kKHNwZWVjaC5saXN0W2ksLW5jb2woc3BlZWNoLmxpc3QpXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbnRlbmNlcz1hcy5jaGFyYWN0ZXIoc2VudGVuY2VzKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3b3JkLmNvdW50LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZW1vdGlvbnMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZW50LmlkPTE6bGVuZ3RoKHNlbnRlbmNlcykNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkNCiAgICApDQogIH0NCn0NCmBgYA0KDQpTb21lIG5vbi1zZW50ZW5jZXMgZXhpc3QgaW4gcmF3IGRhdGEgZHVlIHRvIGVycm9uZW91cyBleHRyYSBlbmQtb2Ygc2VudGVuY2UgbWFya3MuIA0KYGBge3J9DQpzZW50ZW5jZS5saXN0PQ0KICBzZW50ZW5jZS5saXN0JT4lDQogIGZpbHRlcighaXMubmEod29yZC5jb3VudCkpIA0KDQpgYGANCg0KIyBTdGVwIDU6IERhdGEgYW5hbHlzaXMgLS0tIGxlbmd0aCBvZiBzZW50ZW5jZXMNCg0KRm9yIHNpbXBsZXIgdmlzdWFsaXphdGlvbiwgd2UgY2hvc2UgYSBzdWJzZXQgb2YgYmV0dGVyIGtub3duIHByZXNpZGVudHMgb3IgcHJlc2lkZW50aWFsIGNhbmRpZGF0ZXMgb24gd2hpY2ggdG8gZm9jdXMgb3VyIGFuYWx5c2lzLiANCg0KYGBge3J9DQpzZWwuY29tcGFyaXNvbj1jKCJEb25hbGRKVHJ1bXAiLCJKb2huTWNDYWluIiwgIkdlb3JnZUJ1c2giLCAiTWl0dFJvbW5leSIsICJHZW9yZ2VXQnVzaCIsDQogICAgICAgICAgICAgICAgICJSb25hbGRSZWFnYW4iLCJBbGJlcnRHb3JlLEpyIiwgIkhpbGxhcnlDbGludG9uIiwiSm9obkZLZXJyeSIsIA0KICAgICAgICAgICAgICAgICAiV2lsbGlhbUpDbGludG9uIiwiSGFycnlTVHJ1bWFuIiwgIkJhcmFja09iYW1hIiwgIkx5bmRvbkJKb2huc29uIiwNCiAgICAgICAgICAgICAgICAgIkdlcmFsZFJGb3JkIiwgIkppbW15Q2FydGVyIiwgIkR3aWdodERFaXNlbmhvd2VyIiwgIkZyYW5rbGluRFJvb3NldmVsdCIsDQogICAgICAgICAgICAgICAgICJIZXJiZXJ0SG9vdmVyIiwiSm9obkZLZW5uZWR5IiwiUmljaGFyZE5peG9uIiwiV29vZHJvd1dpbHNvbiIsIA0KICAgICAgICAgICAgICAgICAiQWJyYWhhbUxpbmNvbG4iLCAiVGhlb2RvcmVSb29zZXZlbHQiLCAiSmFtZXNHYXJmaWVsZCIsIA0KICAgICAgICAgICAgICAgICAiSm9oblF1aW5jeUFkYW1zIiwgIlVseXNzZXNTR3JhbnQiLCAiVGhvbWFzSmVmZmVyc29uIiwNCiAgICAgICAgICAgICAgICAgIkdlb3JnZVdhc2hpbmd0b24iLCAiV2lsbGlhbUhvd2FyZFRhZnQiLCAiQW5kcmV3SmFja3NvbiIsDQogICAgICAgICAgICAgICAgICJXaWxsaWFtSGVucnlIYXJyaXNvbiIsICJKb2huQWRhbXMiKQ0KYGBgDQoNCiMjIE92ZXJ2aWV3IG9mIHNlbnRlbmNlIGxlbmd0aCBkaXN0cmlidXRpb24gYnkgZGlmZmVyZW50IHR5cGVzIG9mIHNwZWVjaGVzLiANCg0KIyMjIE5vbWluYXRpb24gc3BlZWNoZXMgDQoNCkZpcnN0LCB3ZSBsb29rIGF0ICpub21pbmF0aW9uIGFjY2VwdGFuY2Ugc3BlZWNoZXMqIGF0IG1ham9yIHBhcnR5J3MgbmF0aW9uYWwgY29udmVudGlvbnMuIEZvciByZWxldmFudCB0byBUcnVtcCdzIHNwZWVjaGVzLCB3ZSBsaW1pdCBvdXIgYXR0ZW50aW9uIHRvIHNwZWVjaGVzIGZvciB0aGUgZmlyc3QgdGVybXMgb2YgZm9ybWVyIFUuUy4gcHJlc2lkZW50cy4gIFdlIG5vdGljZWQgdGhhdCBhIG51bWJlciBvZiBwcmVzaWRlbnRzIGhhdmUgdmVyeSBzaG9ydCBzZW50ZW5jZXMgaW4gdGhlaXIgbm9taW5hdGlvbiBhY2NlcHRhbmNlIHNwZWVjaGVzLiANCg0KIyMjIyBGaXJzdCB0ZXJtDQoNCmBgYHtyLCBmaWcud2lkdGggPSA1LCBmaWcuaGVpZ2h0ID0gNH0NCg0KcGFyKG1hcj1jKDIsIDUsIDEsIDEpKQ0KDQojc2VsLmNvbXBhcmlzb249bGV2ZWxzKHNlbnRlbmNlLmxpc3QkRmlsZU9yZGVyZWQpDQpzZW50ZW5jZS5saXN0LnNlbD1maWx0ZXIoc2VudGVuY2UubGlzdCwgDQogICAgICAgICAgICAgICAgICAgICAgICB0eXBlPT0ibm9taW4iLCBUZXJtPT0xLCBGaWxlJWluJXNlbC5jb21wYXJpc29uKQ0Kc2VudGVuY2UubGlzdC5zZWwkRmlsZT1mYWN0b3Ioc2VudGVuY2UubGlzdC5zZWwkRmlsZSkNCg0Kc2VudGVuY2UubGlzdC5zZWwkRmlsZU9yZGVyZWQ9cmVvcmRlcihzZW50ZW5jZS5saXN0LnNlbCRGaWxlLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZW50ZW5jZS5saXN0LnNlbCR3b3JkLmNvdW50LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZWFuLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlcj1UKQ0KDQpiZWVzd2FybSh3b3JkLmNvdW50fkZpbGVPcmRlcmVkLCANCiAgICAgICAgIGRhdGE9c2VudGVuY2UubGlzdC5zZWwsDQogICAgICAgICBob3Jpem9udGFsID0gVFJVRSwgDQogICAgICAgICBwY2g9MTYsIGNvbD1hbHBoYShicmV3ZXIucGFsKDksICJTZXQxIiksIDAuNiksIA0KICAgICAgICAgY2V4PTAuNTUsIGNleC5heGlzPTAuOCwgY2V4LmxhYj0wLjgsDQogICAgICAgICBzcGFjaW5nPTUvbmxldmVscyhzZW50ZW5jZS5saXN0LnNlbCRGaWxlT3JkZXJlZCksDQogICAgICAgICBsYXM9MiwgeGxhYj0iTnVtYmVyIG9mIHdvcmRzIGluIGEgc2VudGVuY2UuIiwgeWxhYj0iIiwNCiAgICAgICAgIG1haW49Ik5vbWluYXRpb24gc3BlZWNoZXMiKQ0KDQpgYGANCg0KIyMjIyBTZWNvbmQgdGVybQ0KDQpgYGB7ciwgZmlnLndpZHRoID0gNSwgZmlnLmhlaWdodCA9IDR9DQoNCnBhcihtYXI9YygyLCA1LCAxLCAxKSkNCg0KI3NlbC5jb21wYXJpc29uPWxldmVscyhzZW50ZW5jZS5saXN0JEZpbGVPcmRlcmVkKQ0Kc2VudGVuY2UubGlzdC5zZWw9ZmlsdGVyKHNlbnRlbmNlLmxpc3QsIA0KICAgICAgICAgICAgICAgICAgICAgICAgdHlwZT09Im5vbWluIiwgVGVybT09MiwgRmlsZSVpbiVzZWwuY29tcGFyaXNvbikNCnNlbnRlbmNlLmxpc3Quc2VsJEZpbGU9ZmFjdG9yKHNlbnRlbmNlLmxpc3Quc2VsJEZpbGUpDQoNCnNlbnRlbmNlLmxpc3Quc2VsJEZpbGVPcmRlcmVkPXJlb3JkZXIoc2VudGVuY2UubGlzdC5zZWwkRmlsZSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VudGVuY2UubGlzdC5zZWwkd29yZC5jb3VudCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWVhbiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXI9VCkNCg0KYmVlc3dhcm0od29yZC5jb3VudH5GaWxlT3JkZXJlZCwgDQogICAgICAgICBkYXRhPXNlbnRlbmNlLmxpc3Quc2VsLA0KICAgICAgICAgaG9yaXpvbnRhbCA9IFRSVUUsIA0KICAgICAgICAgcGNoPTE2LCBjb2w9YWxwaGEoYnJld2VyLnBhbCg5LCAiU2V0MSIpLCAwLjYpLCANCiAgICAgICAgIGNleD0wLjU1LCBjZXguYXhpcz0wLjgsIGNleC5sYWI9MC44LA0KICAgICAgICAgc3BhY2luZz0xLjIvbmxldmVscyhzZW50ZW5jZS5saXN0LnNlbCRGaWxlT3JkZXJlZCksDQogICAgICAgICBsYXM9MiwgeGxhYj0iTnVtYmVyIG9mIHdvcmRzIGluIGEgc2VudGVuY2UuIiwgeWxhYj0iIiwNCiAgICAgICAgIG1haW49Ik5vbWluYXRpb24gc3BlZWNoZXMsIDJuZCB0ZXJtIikNCg0KYGBgDQoNCldoYXQgYXJlIHRoZXNlIHNob3J0IHNlbnRlbmNlcz8NCmBgYHtyfQ0Kc2VudGVuY2UubGlzdCU+JQ0KICBmaWx0ZXIoRmlsZT09IkRvbmFsZEpUcnVtcCIsIA0KICAgICAgICAgdHlwZT09Im5vbWluIiwgDQogICAgICAgICB3b3JkLmNvdW50PD0zKSU+JQ0KICBzZWxlY3Qoc2VudGVuY2VzKSU+JXNhbXBsZV9uKDEwKQ0KDQpzZW50ZW5jZS5saXN0JT4lDQogIGZpbHRlcihGaWxlPT0iQWxiZXJ0R29yZSxKciIsIA0KICAgICAgICAgdHlwZT09Im5vbWluIiwgDQogICAgICAgICB3b3JkLmNvdW50PD0zKSU+JQ0KICBzZWxlY3Qoc2VudGVuY2VzKSU+JXNhbXBsZV9uKDEwKQ0KDQpzZW50ZW5jZS5saXN0JT4lDQogIGZpbHRlcihGaWxlPT0iQ2xpbnRvbiIsIA0KICAgICAgICAgdHlwZT09Im5vbWluIiwgDQogICAgICAgICB3b3JkLmNvdW50PD0zKSU+JQ0KICBzZWxlY3Qoc2VudGVuY2VzKQ0KDQpzZW50ZW5jZS5saXN0JT4lDQogIGZpbHRlcihGaWxlPT0iV2lsbGlhbUpDbGludG9uIiwgDQogICAgICAgICB0eXBlPT0ibm9taW4iLCBUZXJtPT0xLA0KICAgICAgICAgd29yZC5jb3VudDw9MyklPiUNCiAgc2VsZWN0KHNlbnRlbmNlcykNCmBgYA0KDQoNCiMjIyBJbmF1Z3VyYWwgc3BlZWNoZXMNCg0KV2Ugbm90aWNlIHRoYXQgdGhlIHNlbnRlbmNlcyBpbiBpbmF1Z3VyYWwgc3BlZWNoZXMgYXJlIGxvbmdlciB0aGFuIHRob3NlIGluIG5vbWluYXRpb24gYWNjZXB0YW5jZSBzcGVlY2hlcy4gDQoNCmBgYHtyLCBmaWcud2lkdGggPSA1LCBmaWcuaGVpZ2h0ID0gNH0NCnNlbnRlbmNlLmxpc3Quc2VsPXNlbnRlbmNlLmxpc3QlPiVmaWx0ZXIodHlwZT09ImluYXVnIiwgRmlsZSVpbiVzZWwuY29tcGFyaXNvbiwgVGVybT09MSkNCnNlbnRlbmNlLmxpc3Quc2VsJEZpbGU9ZmFjdG9yKHNlbnRlbmNlLmxpc3Quc2VsJEZpbGUpDQoNCnNlbnRlbmNlLmxpc3Quc2VsJEZpbGVPcmRlcmVkPXJlb3JkZXIoc2VudGVuY2UubGlzdC5zZWwkRmlsZSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VudGVuY2UubGlzdC5zZWwkd29yZC5jb3VudCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWVhbiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXI9VCkNCnBhcihtYXI9YygyLCA1LCAxLCAxKSkNCg0KYmVlc3dhcm0od29yZC5jb3VudH5GaWxlT3JkZXJlZCwgDQogICAgICAgICBkYXRhPXNlbnRlbmNlLmxpc3Quc2VsLA0KICAgICAgICAgaG9yaXpvbnRhbCA9IFRSVUUsDQogICAgICAgICBwY2g9MTYsIGNvbD1hbHBoYShicmV3ZXIucGFsKDksICJTZXQxIiksIDAuNiksIA0KICAgICAgICAgY2V4PTAuNTUsIGNleC5heGlzPTAuOCwgY2V4LmxhYj0wLjgsDQogICAgICAgICBzcGFjaW5nPTUvbmxldmVscyhzZW50ZW5jZS5saXN0LnNlbCRGaWxlT3JkZXJlZCksDQogICAgICAgICBsYXM9MiwgeWxhYj0iIiwgeGxhYj0iTnVtYmVyIG9mIHdvcmRzIGluIGEgc2VudGVuY2UuIiwNCiAgICAgICAgIG1haW49IkluYXVndXJhbCBTcGVlY2hlcyIpDQpgYGANCg0KU2hvcnQgc2VudGVuY2VzIGluIGluYXVndXJhbCBzcGVlY2hlcy4gDQpgYGB7cn0NCnNlbnRlbmNlLmxpc3QlPiUNCiAgZmlsdGVyKEZpbGU9PSJCYXJhY2tPYmFtYSIsIA0KICAgICAgICAgdHlwZT09ImluYXVnIiwgDQogICAgICAgICB3b3JkLmNvdW50PD0zKSU+JQ0KICBzZWxlY3Qoc2VudGVuY2VzKQ0KYGBgDQoNCg0KIyBTdGVwIDU6IERhdGEgYW5hbHlzaXMgLS0tIHNlbnRpbWVudCBhbmFsc2lzDQoNCiMjIFNlbnRlbmNlIGxlbmd0aCB2YXJpYXRpb24gb3ZlciB0aGUgY291cnNlIG9mIHRoZSBzcGVlY2gsIHdpdGggZW1vdGlvbnMuIA0KDQpIb3cgb3VyIHByZXNpZGVudHMgKG9yIGNhbmRpZGF0ZXMpIGFsdGVybmF0ZSBiZXR3ZWVuIGxvbmcgYW5kIHNob3J0IHNlbnRlbmNlcyBhbmQgaG93IHRoZXkgc2hpZnQgYmV0d2VlbiBkaWZmZXJlbnQgc2VudGltZW50cyBpbiB0aGVpciBzcGVlY2hlcy4gSXQgaXMgaW50ZXJlc3RpbmcgdG8gbm90ZSB0aGF0IHNvbWUgcHJlc2lkZW50aWFsIGNhbmRpZGF0ZXMnIHNwZWVjaCBhcmUgbW9yZSBjb2xvcmZ1bCB0aGFuIG90aGVycy4gSGVyZSB3ZSB1c2VkIHRoZSBzYW1lIGNvbG9yIHRoZW1lIGFzIGluIHRoZSBtb3ZpZSAiSW5zaWRlIE91dC4iDQoNCiFbaW1hZ2VdKGh0dHA6Ly93d3cuc3RhZmZvcmRzY2hvb2xzLm5ldC9jbXMvbGliMDExL1ZBMDE4MTg3MjMvQ2VudHJpY2l0eS9Eb21haW4vMzU3NC9jaGFyYWN0ZXJfaWNvbi5wbmcpDQoNCmBgYHtyLCBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9OH0NCnBhcihtZnJvdz1jKDQsMSksIG1hcj1jKDAsMCwyLDApLCBidHk9Im4iLCB4YXh0PSJuIiwgeWF4dD0ibiIsIGZvbnQubWFpbj0xKQ0KDQpmLnBsb3RzZW50LmxlbihJbi5saXN0PXNlbnRlbmNlLmxpc3QsIEluRmlsZT0iSGlsbGFyeUNsaW50b24iLCANCiAgICAgICAgICAgICAgIEluVHlwZT0ibm9taW4iLCBJblRlcm09MSwgUHJlc2lkZW50PSJIaWxsYXJ5IENsaW50b24iKQ0KDQpmLnBsb3RzZW50LmxlbihJbi5saXN0PXNlbnRlbmNlLmxpc3QsIEluRmlsZT0iRG9uYWxkSlRydW1wIiwgDQogICAgICAgICAgICAgICBJblR5cGU9Im5vbWluIiwgSW5UZXJtPTEsIFByZXNpZGVudD0iRG9uYWxkIFRydW1wIikNCg0KZi5wbG90c2VudC5sZW4oSW4ubGlzdD1zZW50ZW5jZS5saXN0LCBJbkZpbGU9IkJhcmFja09iYW1hIiwgDQogICAgICAgICAgICAgICBJblR5cGU9Im5vbWluIiwgSW5UZXJtPTEsIFByZXNpZGVudD0iQmFyYWNrIE9iYW1hIikNCg0KZi5wbG90c2VudC5sZW4oSW4ubGlzdD1zZW50ZW5jZS5saXN0LCBJbkZpbGU9Ikdlb3JnZVdCdXNoIiwgDQogICAgICAgICAgICAgICBJblR5cGU9Im5vbWluIiwgSW5UZXJtPTEsIFByZXNpZGVudD0iR2VvcmdlIFcuIEJ1c2giKQ0KYGBgDQoNCiMjIyBXaGF0IGFyZSB0aGUgZW1vdGlvbmFsbHkgY2hhcmdlZCBzZW50ZW5jZXM/DQoNCmBgYHtyfQ0KcHJpbnQoIkhpbGxhcnkgQ2xpbnRvbiIpDQpzcGVlY2guZGY9dGJsX2RmKHNlbnRlbmNlLmxpc3QpJT4lDQogIGZpbHRlcihGaWxlPT0iSGlsbGFyeUNsaW50b24iLCB0eXBlPT0ibm9taW4iLCB3b3JkLmNvdW50Pj00KSU+JQ0KICBzZWxlY3Qoc2VudGVuY2VzLCBhbmdlcjp0cnVzdCkNCnNwZWVjaC5kZj1hcy5kYXRhLmZyYW1lKHNwZWVjaC5kZikNCmFzLmNoYXJhY3RlcihzcGVlY2guZGYkc2VudGVuY2VzW2FwcGx5KHNwZWVjaC5kZlssLTFdLCAyLCB3aGljaC5tYXgpXSkNCg0KcHJpbnQoIkJhcmFjayBPYmFtYSIpDQpzcGVlY2guZGY9dGJsX2RmKHNlbnRlbmNlLmxpc3QpJT4lDQogIGZpbHRlcihGaWxlPT0iQmFyYWNrT2JhbWEiLCB0eXBlPT0ibm9taW4iLCBUZXJtPT0xLCB3b3JkLmNvdW50Pj01KSU+JQ0KICBzZWxlY3Qoc2VudGVuY2VzLCBhbmdlcjp0cnVzdCkNCnNwZWVjaC5kZj1hcy5kYXRhLmZyYW1lKHNwZWVjaC5kZikNCmFzLmNoYXJhY3RlcihzcGVlY2guZGYkc2VudGVuY2VzW2FwcGx5KHNwZWVjaC5kZlssLTFdLCAyLCB3aGljaC5tYXgpXSkNCg0KcHJpbnQoIkdlb3JnZSBXIEJ1c2giKQ0Kc3BlZWNoLmRmPXRibF9kZihzZW50ZW5jZS5saXN0KSU+JQ0KICBmaWx0ZXIoRmlsZT09Ikdlb3JnZVdCdXNoIiwgdHlwZT09Im5vbWluIiwgVGVybT09MSwgd29yZC5jb3VudD49NCklPiUNCiAgc2VsZWN0KHNlbnRlbmNlcywgYW5nZXI6dHJ1c3QpDQpzcGVlY2guZGY9YXMuZGF0YS5mcmFtZShzcGVlY2guZGYpDQphcy5jaGFyYWN0ZXIoc3BlZWNoLmRmJHNlbnRlbmNlc1thcHBseShzcGVlY2guZGZbLC0xXSwgMiwgd2hpY2gubWF4KV0pDQoNCnByaW50KCJEb25hbGQgVHJ1bXAiKQ0Kc3BlZWNoLmRmPXRibF9kZihzZW50ZW5jZS5saXN0KSU+JQ0KICBmaWx0ZXIoRmlsZT09IkRvbmFsZEpUcnVtcCIsIHR5cGU9PSJub21pbiIsIFRlcm09PTEsIHdvcmQuY291bnQ+PTUpJT4lDQogIHNlbGVjdChzZW50ZW5jZXMsIGFuZ2VyOnRydXN0KQ0Kc3BlZWNoLmRmPWFzLmRhdGEuZnJhbWUoc3BlZWNoLmRmKQ0KYXMuY2hhcmFjdGVyKHNwZWVjaC5kZiRzZW50ZW5jZXNbYXBwbHkoc3BlZWNoLmRmWywtMV0sIDIsIHdoaWNoLm1heCldKQ0KDQpgYGANCg0KDQojIyBDbHVzdGVyaW5nIG9mIGVtb3Rpb25zDQpgYGB7ciwgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9NX0NCmhlYXRtYXAuMihjb3Ioc2VudGVuY2UubGlzdCU+JWZpbHRlcih0eXBlPT0iaW5hdWciKSU+JXNlbGVjdChhbmdlcjp0cnVzdCkpLCANCiAgICAgICAgICBzY2FsZSA9ICJub25lIiwgDQogICAgICAgICAgY29sID0gYmx1ZXJlZCgxMDApLCAsIG1hcmdpbj1jKDYsIDYpLCBrZXk9RiwNCiAgICAgICAgICB0cmFjZSA9ICJub25lIiwgZGVuc2l0eS5pbmZvID0gIm5vbmUiKQ0KDQpwYXIobWFyPWMoNCwgNiwgMiwgMSkpDQplbW8ubWVhbnM9Y29sTWVhbnMoc2VsZWN0KHNlbnRlbmNlLmxpc3QsIGFuZ2VyOnRydXN0KT4wLjAxKQ0KY29sLnVzZT1jKCJyZWQyIiwgImRhcmtnb2xkZW5yb2QxIiwgDQogICAgICAgICAgICAiY2hhcnRyZXVzZTMiLCAiYmx1ZXZpb2xldCIsDQogICAgICAgICAgICAiZGFya2dvbGRlbnJvZDIiLCAiZG9kZ2VyYmx1ZTMiLCANCiAgICAgICAgICAgICJkYXJrZ29sZGVucm9kMSIsICJkYXJrZ29sZGVucm9kMSIpDQpiYXJwbG90KGVtby5tZWFuc1tvcmRlcihlbW8ubWVhbnMpXSwgbGFzPTIsIGNvbD1jb2wudXNlW29yZGVyKGVtby5tZWFucyldLCBob3Jpej1ULCBtYWluPSJJbmF1Z3VyYWwgU3BlZWNoZXMiKQ0KYGBgDQoNCmBgYHtyLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD02fQ0KcHJlc2lkLnN1bW1hcnk9dGJsX2RmKHNlbnRlbmNlLmxpc3QpJT4lDQogIGZpbHRlcih0eXBlPT0ibm9taW4iLCBGaWxlJWluJXNlbC5jb21wYXJpc29uKSU+JQ0KICAjZ3JvdXBfYnkocGFzdGUwKHR5cGUsIEZpbGUpKSU+JQ0KICBncm91cF9ieShGaWxlKSU+JQ0KICBzdW1tYXJpc2UoDQogICAgYW5nZXI9bWVhbihhbmdlciksDQogICAgYW50aWNpcGF0aW9uPW1lYW4oYW50aWNpcGF0aW9uKSwNCiAgICBkaXNndXN0PW1lYW4oZGlzZ3VzdCksDQogICAgZmVhcj1tZWFuKGZlYXIpLA0KICAgIGpveT1tZWFuKGpveSksDQogICAgc2FkbmVzcz1tZWFuKHNhZG5lc3MpLA0KICAgIHN1cnByaXNlPW1lYW4oc3VycHJpc2UpLA0KICAgIHRydXN0PW1lYW4odHJ1c3QpDQogICAgI25lZ2F0aXZlPW1lYW4obmVnYXRpdmUpLA0KICAgICNwb3NpdGl2ZT1tZWFuKHBvc2l0aXZlKQ0KICApDQoNCnByZXNpZC5zdW1tYXJ5PWFzLmRhdGEuZnJhbWUocHJlc2lkLnN1bW1hcnkpDQpyb3duYW1lcyhwcmVzaWQuc3VtbWFyeSk9YXMuY2hhcmFjdGVyKChwcmVzaWQuc3VtbWFyeVssMV0pKQ0Ka20ucmVzPWttZWFucyhwcmVzaWQuc3VtbWFyeVssLTFdLCBpdGVyLm1heD0yMDAsDQogICAgICAgICAgICAgIDUpDQpmdml6X2NsdXN0ZXIoa20ucmVzLCANCiAgICAgICAgICAgICBzdGFuZD1GLCByZXBlbD0gVFJVRSwNCiAgICAgICAgICAgICBkYXRhID0gcHJlc2lkLnN1bW1hcnlbLC0xXSwgeGxhYj0iIiwgeGF4dD0ibiIsDQogICAgICAgICAgICAgc2hvdy5jbHVzdC5jZW50PUZBTFNFKQ0KYGBgDQoNCiMgU3RlcCA1OiBEYXRhIGFuYWx5c2lzIC0tLSBUb3BpYyBtb2RlbGluZw0KDQpGb3IgdG9waWMgbW9kZWxpbmcsIHdlIHByZXBhcmUgYSBjb3JwdXMgb2Ygc2VudGVuY2Ugc25pcGV0cyBhcyBmb2xsb3dzLiBGb3IgZWFjaCBzcGVlY2gsIHdlIHN0YXJ0IHdpdGggc2VudGVuY2VzIGFuZCBwcmVwYXJlIGEgc25pcGV0IHdpdGggYSBnaXZlbiBzZW50ZW5jZSB3aXRoIHRoZSBmbGFua2luZyBzZW50ZW5jZXMuIA0KDQpgYGB7cn0NCmNvcnB1cy5saXN0PXNlbnRlbmNlLmxpc3RbMjoobnJvdyhzZW50ZW5jZS5saXN0KS0xKSwgXQ0Kc2VudGVuY2UucHJlPXNlbnRlbmNlLmxpc3Qkc2VudGVuY2VzWzE6KG5yb3coc2VudGVuY2UubGlzdCktMildDQpzZW50ZW5jZS5wb3N0PXNlbnRlbmNlLmxpc3Qkc2VudGVuY2VzWzM6KG5yb3coc2VudGVuY2UubGlzdCktMSldDQpjb3JwdXMubGlzdCRzbmlwZXRzPXBhc3RlKHNlbnRlbmNlLnByZSwgY29ycHVzLmxpc3Qkc2VudGVuY2VzLCBzZW50ZW5jZS5wb3N0LCBzZXA9IiAiKQ0Kcm0ucm93cz0oMTpucm93KGNvcnB1cy5saXN0KSlbY29ycHVzLmxpc3Qkc2VudC5pZD09MV0NCnJtLnJvd3M9YyhybS5yb3dzLCBybS5yb3dzLTEpDQpjb3JwdXMubGlzdD1jb3JwdXMubGlzdFstcm0ucm93cywgXQ0KYGBgDQoNCiMjIFRleHQgbWluaW5nDQpgYGB7cn0NCmRvY3MgPC0gQ29ycHVzKFZlY3RvclNvdXJjZShjb3JwdXMubGlzdCRzbmlwZXRzKSkNCndyaXRlTGluZXMoYXMuY2hhcmFjdGVyKGRvY3NbW3NhbXBsZSgxOm5yb3coY29ycHVzLmxpc3QpLCAxKV1dKSkNCmBgYA0KDQojIyMgVGV4dCBiYXNpYyBwcm9jZXNzaW5nDQpBZGFwdGVkIGZyb20gPGh0dHBzOi8vZWlnaHQybGF0ZS53b3JkcHJlc3MuY29tLzIwMTUvMDkvMjkvYS1nZW50bGUtaW50cm9kdWN0aW9uLXRvLXRvcGljLW1vZGVsaW5nLXVzaW5nLXIvPi4NCg0KYGBge3J9DQojcmVtb3ZlIHBvdGVudGlhbGx5IHByb2JsZW1hdGljIHN5bWJvbHMNCmRvY3MgPC10bV9tYXAoZG9jcyxjb250ZW50X3RyYW5zZm9ybWVyKHRvbG93ZXIpKQ0Kd3JpdGVMaW5lcyhhcy5jaGFyYWN0ZXIoZG9jc1tbc2FtcGxlKDE6bnJvdyhjb3JwdXMubGlzdCksIDEpXV0pKQ0KDQojcmVtb3ZlIHB1bmN0dWF0aW9uDQpkb2NzIDwtIHRtX21hcChkb2NzLCByZW1vdmVQdW5jdHVhdGlvbikNCndyaXRlTGluZXMoYXMuY2hhcmFjdGVyKGRvY3NbW3NhbXBsZSgxOm5yb3coY29ycHVzLmxpc3QpLCAxKV1dKSkNCg0KI1N0cmlwIGRpZ2l0cw0KZG9jcyA8LSB0bV9tYXAoZG9jcywgcmVtb3ZlTnVtYmVycykNCndyaXRlTGluZXMoYXMuY2hhcmFjdGVyKGRvY3NbW3NhbXBsZSgxOm5yb3coY29ycHVzLmxpc3QpLCAxKV1dKSkNCg0KI3JlbW92ZSBzdG9wd29yZHMNCmRvY3MgPC0gdG1fbWFwKGRvY3MsIHJlbW92ZVdvcmRzLCBzdG9wd29yZHMoImVuZ2xpc2giKSkNCndyaXRlTGluZXMoYXMuY2hhcmFjdGVyKGRvY3NbW3NhbXBsZSgxOm5yb3coY29ycHVzLmxpc3QpLCAxKV1dKSkNCg0KI3JlbW92ZSB3aGl0ZXNwYWNlDQpkb2NzIDwtIHRtX21hcChkb2NzLCBzdHJpcFdoaXRlc3BhY2UpDQp3cml0ZUxpbmVzKGFzLmNoYXJhY3Rlcihkb2NzW1tzYW1wbGUoMTpucm93KGNvcnB1cy5saXN0KSwgMSldXSkpDQoNCiNTdGVtIGRvY3VtZW50DQpkb2NzIDwtIHRtX21hcChkb2NzLHN0ZW1Eb2N1bWVudCkNCndyaXRlTGluZXMoYXMuY2hhcmFjdGVyKGRvY3NbW3NhbXBsZSgxOm5yb3coY29ycHVzLmxpc3QpLCAxKV1dKSkNCmBgYA0KDQojIyMgVG9waWMgbW9kZWxpbmcNCg0KR2VuZ2VyYXRlIGRvY3VtZW50LXRlcm0gbWF0cmljZXMuIA0KDQpgYGB7cn0NCmR0bSA8LSBEb2N1bWVudFRlcm1NYXRyaXgoZG9jcykNCiNjb252ZXJ0IHJvd25hbWVzIHRvIGZpbGVuYW1lcyNjb252ZXJ0IHJvd25hbWVzIHRvIGZpbGVuYW1lcw0Kcm93bmFtZXMoZHRtKSA8LSBwYXN0ZShjb3JwdXMubGlzdCR0eXBlLCBjb3JwdXMubGlzdCRGaWxlLA0KICAgICAgICAgICAgICAgICAgICAgICBjb3JwdXMubGlzdCRUZXJtLCBjb3JwdXMubGlzdCRzZW50LmlkLCBzZXA9Il8iKQ0KDQpyb3dUb3RhbHMgPC0gYXBwbHkoZHRtICwgMSwgc3VtKSAjRmluZCB0aGUgc3VtIG9mIHdvcmRzIGluIGVhY2ggRG9jdW1lbnQNCg0KZHRtICA8LSBkdG1bcm93VG90YWxzPiAwLCBdDQpjb3JwdXMubGlzdD1jb3JwdXMubGlzdFtyb3dUb3RhbHM+MCwgXQ0KDQpgYGANCg0KUnVuIExEQQ0KDQpgYGB7cn0NCiNTZXQgcGFyYW1ldGVycyBmb3IgR2liYnMgc2FtcGxpbmcNCmJ1cm5pbiA8LSA0MDAwDQppdGVyIDwtIDIwMDANCnRoaW4gPC0gNTAwDQpzZWVkIDwtbGlzdCgyMDAzLDUsNjMsMTAwMDAxLDc2NSkNCm5zdGFydCA8LSA1DQpiZXN0IDwtIFRSVUUNCg0KI051bWJlciBvZiB0b3BpY3MNCmsgPC0gMTUNCg0KI1J1biBMREEgdXNpbmcgR2liYnMgc2FtcGxpbmcNCmxkYU91dCA8LUxEQShkdG0sIGssIG1ldGhvZD0iR2liYnMiLCBjb250cm9sPWxpc3QobnN0YXJ0PW5zdGFydCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VlZCA9IHNlZWQsIGJlc3Q9YmVzdCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBidXJuaW4gPSBidXJuaW4sIGl0ZXIgPSBpdGVyLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGluPXRoaW4pKQ0KI3dyaXRlIG91dCByZXN1bHRzDQojZG9jcyB0byB0b3BpY3MNCmxkYU91dC50b3BpY3MgPC0gYXMubWF0cml4KHRvcGljcyhsZGFPdXQpKQ0KdGFibGUoYygxOmssIGxkYU91dC50b3BpY3MpKQ0Kd3JpdGUuY3N2KGxkYU91dC50b3BpY3MsZmlsZT1wYXN0ZSgiLi4vb3V0L0xEQUdpYmJzIixrLCJEb2NzVG9Ub3BpY3MuY3N2IikpDQoNCiN0b3AgNiB0ZXJtcyBpbiBlYWNoIHRvcGljDQpsZGFPdXQudGVybXMgPC0gYXMubWF0cml4KHRlcm1zKGxkYU91dCwyMCkpDQp3cml0ZS5jc3YobGRhT3V0LnRlcm1zLGZpbGU9cGFzdGUoIi4uL291dC9MREFHaWJicyIsaywiVG9waWNzVG9UZXJtcy5jc3YiKSkNCg0KI3Byb2JhYmlsaXRpZXMgYXNzb2NpYXRlZCB3aXRoIGVhY2ggdG9waWMgYXNzaWdubWVudA0KdG9waWNQcm9iYWJpbGl0aWVzIDwtIGFzLmRhdGEuZnJhbWUobGRhT3V0QGdhbW1hKQ0Kd3JpdGUuY3N2KHRvcGljUHJvYmFiaWxpdGllcyxmaWxlPXBhc3RlKCIuLi9vdXQvTERBR2liYnMiLGssIlRvcGljUHJvYmFiaWxpdGllcy5jc3YiKSkNCmBgYA0KYGBge3J9DQp0ZXJtcy5iZXRhPWxkYU91dEBiZXRhDQp0ZXJtcy5iZXRhPXNjYWxlKHRlcm1zLmJldGEpDQp0b3BpY3MudGVybXM9TlVMTA0KZm9yKGkgaW4gMTprKXsNCiAgdG9waWNzLnRlcm1zPXJiaW5kKHRvcGljcy50ZXJtcywgbGRhT3V0QHRlcm1zW29yZGVyKHRlcm1zLmJldGFbaSxdLCBkZWNyZWFzaW5nID0gVFJVRSlbMTo3XV0pDQp9DQp0b3BpY3MudGVybXMNCmxkYU91dC50ZXJtcw0KYGBgDQoNCkJhc2VkIG9uIHRoZSBtb3N0IHBvcHVsYXIgdGVybXMgYW5kIHRoZSBtb3N0IHNhbGllbnQgdGVybXMgZm9yIGVhY2ggdG9waWMsIHdlIGFzc2lnbiBhIGhhc2h0YWcgdG8gZWFjaCB0b3BpYy4gVGhpcyBwYXJ0IHJlcXVpcmUgbWFudWFsIHNldHVwIGFzIHRoZSB0b3BpY3MgYXJlIGxpa2VseSB0byBjaGFuZ2UuIA0KDQpgYGB7cn0NCnRvcGljcy5oYXNoPWMoIkVjb25vbXkiLCAiQW1lcmljYSIsICJEZWZlbnNlIiwgIkJlbGllZiIsICJFbGVjdGlvbiIsICJQYXRyaW90aXNtIiwgIlVuaXR5IiwgIkdvdmVybm1lbnQiLCAiUmVmb3JtIiwgIlRlbXBvcmFsIiwgIldvcmtpbmdGYW1pbGllcyIsICJGcmVlZG9tIiwgIkVxdWFsaXR5IiwgIk1pc2MiLCAiTGVnaXNsYXRpb24iKQ0KY29ycHVzLmxpc3QkbGRhdG9waWM9YXMudmVjdG9yKGxkYU91dC50b3BpY3MpDQpjb3JwdXMubGlzdCRsZGFoYXNoPXRvcGljcy5oYXNoW2xkYU91dC50b3BpY3NdDQoNCmNvbG5hbWVzKHRvcGljUHJvYmFiaWxpdGllcyk9dG9waWNzLmhhc2gNCmNvcnB1cy5saXN0LmRmPWNiaW5kKGNvcnB1cy5saXN0LCB0b3BpY1Byb2JhYmlsaXRpZXMpDQpgYGANCg0KIyMgQ2x1c3RlcmluZyBvZiB0b3BpY3MNCmBgYHtyLCBmaWcud2lkdGg9NiwgZmlnLmhlaWdodD04fQ0KcGFyKG1hcj1jKDEsMSwxLDEpKQ0KdG9waWMuc3VtbWFyeT10YmxfZGYoY29ycHVzLmxpc3QuZGYpJT4lDQogICAgICAgICAgICAgIGZpbHRlcih0eXBlJWluJWMoIm5vbWluIiwgImluYXVnIiksIEZpbGUlaW4lc2VsLmNvbXBhcmlzb24pJT4lDQogICAgICAgICAgICAgIHNlbGVjdChGaWxlLCBFY29ub215OkxlZ2lzbGF0aW9uKSU+JQ0KICAgICAgICAgICAgICBncm91cF9ieShGaWxlKSU+JQ0KICAgICAgICAgICAgICBzdW1tYXJpc2VfZWFjaChmdW5zKG1lYW4pKQ0KdG9waWMuc3VtbWFyeT1hcy5kYXRhLmZyYW1lKHRvcGljLnN1bW1hcnkpDQpyb3duYW1lcyh0b3BpYy5zdW1tYXJ5KT10b3BpYy5zdW1tYXJ5WywxXQ0KDQojIFsxXSAiRWNvbm9teSIgICAgICAgICAiQW1lcmljYSIgICAgICAgICAiRGVmZW5zZSIgICAgICAgICAiQmVsaWVmIiAgICAgICAgIA0KIyBbNV0gIkVsZWN0aW9uIiAgICAgICAgIlBhdHJpb3Rpc20iICAgICAgIlVuaXR5IiAgICAgICAgICAgIkdvdmVybm1lbnQiICAgICANCiMgWzldICJSZWZvcm0iICAgICAgICAgICJUZW1wb3JhbCIgICAgICAgICJXb3JraW5nRmFtaWxpZXMiICJGcmVlZG9tIiAgICAgICAgDQojIFsxM10gIkVxdWFsaXR5IiAgICAgICAgIk1pc2MiICAgICAgICAgICAgIkxlZ2lzbGF0aW9uIiAgICAgICANCg0KdG9waWMucGxvdD1jKDEsIDEzLCA5LCAxMSwgOCwgMywgNykNCnByaW50KHRvcGljcy5oYXNoW3RvcGljLnBsb3RdKQ0KDQpoZWF0bWFwLjIoYXMubWF0cml4KHRvcGljLnN1bW1hcnlbLHRvcGljLnBsb3QrMV0pLCANCiAgICAgICAgICBzY2FsZSA9ICJjb2x1bW4iLCBrZXk9RiwgDQogICAgICAgICAgY29sID0gYmx1ZXJlZCgxMDApLA0KICAgICAgICAgIGNleFJvdyA9IDAuOSwgY2V4Q29sID0gMC45LCBtYXJnaW5zID0gYyg4LCA4KSwNCiAgICAgICAgICB0cmFjZSA9ICJub25lIiwgZGVuc2l0eS5pbmZvID0gIm5vbmUiKQ0KYGBgDQoNCmBgYHtyLCBmaWcud2lkdGg9NiwgZmlnLmhlaWdodD0xMH0NCiMgWzFdICJFY29ub215IiAgICAgICAgICJBbWVyaWNhIiAgICAgICAgICJEZWZlbnNlIiAgICAgICAgICJCZWxpZWYiICAgICAgICAgDQojIFs1XSAiRWxlY3Rpb24iICAgICAgICAiUGF0cmlvdGlzbSIgICAgICAiVW5pdHkiICAgICAgICAgICAiR292ZXJubWVudCIgICAgIA0KIyBbOV0gIlJlZm9ybSIgICAgICAgICAgIlRlbXBvcmFsIiAgICAgICAgIldvcmtpbmdGYW1pbGllcyIgIkZyZWVkb20iICAgICAgICANCiMgWzEzXSAiRXF1YWxpdHkiICAgICAgICAiTWlzYyIgICAgICAgICAgICAiTGVnaXNsYXRpb24iICAgICAgIA0KIA0KDQpwYXIobWZyb3c9Yyg1LCAxKSwgbWFyPWMoMSwxLDIsMCksIGJ0eT0ibiIsIHhheHQ9Im4iLCB5YXh0PSJuIikNCg0KdG9waWMucGxvdD1jKDEsIDEzLCAxNCwgMTUsIDgsIDksIDEyKQ0KcHJpbnQodG9waWNzLmhhc2hbdG9waWMucGxvdF0pDQoNCnNwZWVjaC5kZj10YmxfZGYoY29ycHVzLmxpc3QuZGYpJT4lZmlsdGVyKEZpbGU9PSJHZW9yZ2VCdXNoIiwgdHlwZT09Im5vbWluIixUZXJtPT0xKSU+JXNlbGVjdChzZW50LmlkLCBFY29ub215OkxlZ2lzbGF0aW9uKQ0Kc3BlZWNoLmRmPWFzLm1hdHJpeChzcGVlY2guZGYpDQpzcGVlY2guZGZbLC0xXT1yZXBsYWNlKHNwZWVjaC5kZlssLTFdLCBzcGVlY2guZGZbLC0xXTwxLzE1LCAwLjAwMSkNCnNwZWVjaC5kZlssLTFdPWYuc21vb3RoLnRvcGljKHg9c3BlZWNoLmRmWywxXSwgeT1zcGVlY2guZGZbLC0xXSkNCnBsb3Quc3RhY2tlZChzcGVlY2guZGZbLDFdLCBzcGVlY2guZGZbLHRvcGljLnBsb3QrMV0sIA0KICAgICAgICAgICAgIHhsYWI9IlNlbnRlbmNlcyIsIHlsYWI9IlRvcGljIHNoYXJlIiwgbWFpbj0iR2VvcmdlIEJ1c2gsIE5vbWluYXRpb24iKQ0KDQpzcGVlY2guZGY9dGJsX2RmKGNvcnB1cy5saXN0LmRmKSU+JWZpbHRlcihGaWxlPT0iV2lsbGlhbUpDbGludG9uIiwgdHlwZT09Im5vbWluIiwgVGVybT09MSklPiVzZWxlY3Qoc2VudC5pZCwgRWNvbm9teTpMZWdpc2xhdGlvbikNCnNwZWVjaC5kZj1hcy5tYXRyaXgoc3BlZWNoLmRmKQ0Kc3BlZWNoLmRmWywtMV09cmVwbGFjZShzcGVlY2guZGZbLC0xXSwgc3BlZWNoLmRmWywtMV08MS8xNSwgMC4wMDEpDQpzcGVlY2guZGZbLC0xXT1mLnNtb290aC50b3BpYyh4PXNwZWVjaC5kZlssMV0sIHk9c3BlZWNoLmRmWywtMV0pDQpwbG90LnN0YWNrZWQoc3BlZWNoLmRmWywxXSwgc3BlZWNoLmRmWyx0b3BpYy5wbG90KzFdLA0KICAgICAgICAgICAgeGxhYj0iU2VudGVuY2VzIiwgeWxhYj0iVG9waWMgc2hhcmUiLCBtYWluPSJCaWxsIENsaW50b24sIE5vbWluYXRpb24iKQ0KDQpzcGVlY2guZGY9dGJsX2RmKGNvcnB1cy5saXN0LmRmKSU+JWZpbHRlcihGaWxlPT0iR2VvcmdlV0J1c2giLCB0eXBlPT0ibm9taW4iLCBUZXJtPT0xKSU+JXNlbGVjdChzZW50LmlkLCBFY29ub215OkxlZ2lzbGF0aW9uKQ0Kc3BlZWNoLmRmPWFzLm1hdHJpeChzcGVlY2guZGYpDQpzcGVlY2guZGZbLC0xXT1yZXBsYWNlKHNwZWVjaC5kZlssLTFdLCBzcGVlY2guZGZbLC0xXTwxLzE1LCAwLjAwMSkNCnNwZWVjaC5kZlssLTFdPWYuc21vb3RoLnRvcGljKHg9c3BlZWNoLmRmWywxXSwgeT1zcGVlY2guZGZbLC0xXSkNCnBsb3Quc3RhY2tlZChzcGVlY2guZGZbLDFdLCBzcGVlY2guZGZbLHRvcGljLnBsb3QrMV0sIA0KICAgICAgICAgICAgeGxhYj0iU2VudGVuY2VzIiwgeWxhYj0iVG9waWMgc2hhcmUiLCBtYWluPSJHZW9yZ2UgVyBCdXNoLCBOb21pbmF0aW9uIikNCg0Kc3BlZWNoLmRmPXRibF9kZihjb3JwdXMubGlzdC5kZiklPiVmaWx0ZXIoRmlsZT09IkJhcmFja09iYW1hIiwgdHlwZT09Im5vbWluIiwgVGVybT09MSklPiVzZWxlY3Qoc2VudC5pZCwgRWNvbm9teTpMZWdpc2xhdGlvbikNCnNwZWVjaC5kZj1hcy5tYXRyaXgoc3BlZWNoLmRmKQ0Kc3BlZWNoLmRmWywtMV09cmVwbGFjZShzcGVlY2guZGZbLC0xXSwgc3BlZWNoLmRmWywtMV08MS8xNSwgMC4wMDEpDQpzcGVlY2guZGZbLC0xXT1mLnNtb290aC50b3BpYyh4PXNwZWVjaC5kZlssMV0sIHk9c3BlZWNoLmRmWywtMV0pDQpwbG90LnN0YWNrZWQoc3BlZWNoLmRmWywxXSwgc3BlZWNoLmRmWyx0b3BpYy5wbG90KzFdLA0KICAgICAgICAgICAgeGxhYj0iU2VudGVuY2VzIiwgeWxhYj0iVG9waWMgc2hhcmUiLCBtYWluPSJCYXJhY2sgT2JhbWEsIE5vbWluYXRpb24iKQ0KDQpzcGVlY2guZGY9dGJsX2RmKGNvcnB1cy5saXN0LmRmKSU+JWZpbHRlcihGaWxlPT0iRG9uYWxkSlRydW1wIiwgdHlwZT09Im5vbWluIiklPiVzZWxlY3Qoc2VudC5pZCwgRWNvbm9teTpMZWdpc2xhdGlvbikNCnNwZWVjaC5kZj1hcy5tYXRyaXgoc3BlZWNoLmRmKQ0Kc3BlZWNoLmRmWywtMV09cmVwbGFjZShzcGVlY2guZGZbLC0xXSwgc3BlZWNoLmRmWywtMV08MS8xNSwgMC4wMDEpDQpzcGVlY2guZGZbLC0xXT1mLnNtb290aC50b3BpYyh4PXNwZWVjaC5kZlssMV0sIHk9c3BlZWNoLmRmWywtMV0pDQpwbG90LnN0YWNrZWQoc3BlZWNoLmRmWywxXSwgc3BlZWNoLmRmWyx0b3BpYy5wbG90KzFdLA0KICAgICAgICAgICAgeGxhYj0iU2VudGVuY2VzIiwgeWxhYj0iVG9waWMgc2hhcmUiLCBtYWluPSJEb25hbGQgVHJ1bXAsIE5vbWluYXRpb24iKQ0KYGBgDQoNCmBgYHtyLCBmaWcud2lkdGg9NiwgZmlnLmhlaWdodD0xMH0NCiMgWzFdICJFY29ub215IiAgICAgICAgICJBbWVyaWNhIiAgICAgICAgICJEZWZlbnNlIiAgICAgICAgICJCZWxpZWYiICAgICAgICAgDQojIFs1XSAiRWxlY3Rpb24iICAgICAgICAiUGF0cmlvdGlzbSIgICAgICAiVW5pdHkiICAgICAgICAgICAiR292ZXJubWVudCIgICAgIA0KIyBbOV0gIlJlZm9ybSIgICAgICAgICAgIlRlbXBvcmFsIiAgICAgICAgIldvcmtpbmdGYW1pbGllcyIgIkZyZWVkb20iICAgICAgICANCiMgWzEzXSAiRXF1YWxpdHkiICAgICAgICAiTWlzYyIgICAgICAgICAgICAiTGVnaXNsYXRpb24iICAgICAgIA0KDQoNCnBhcihtZnJvdz1jKDUsIDEpLCBtYXI9YygxLDEsMiwwKSwgYnR5PSJuIiwgeGF4dD0ibiIsIHlheHQ9Im4iKQ0KDQoNCnRvcGljLnBsb3Q9YygxLCAxMywgMTQsIDE1LCA4LCA5LCAxMikNCnByaW50KHRvcGljcy5oYXNoW3RvcGljLnBsb3RdKQ0KDQpzcGVlY2guZGY9dGJsX2RmKGNvcnB1cy5saXN0LmRmKSU+JWZpbHRlcihGaWxlPT0iR2VvcmdlQnVzaCIsIHR5cGU9PSJpbmF1ZyIsIFRlcm09PTEpJT4lc2VsZWN0KHNlbnQuaWQsIEVjb25vbXk6TGVnaXNsYXRpb24pDQpzcGVlY2guZGY9YXMubWF0cml4KHNwZWVjaC5kZikNCnNwZWVjaC5kZlssLTFdPXJlcGxhY2Uoc3BlZWNoLmRmWywtMV0sIHNwZWVjaC5kZlssLTFdPDEvMTUsIDAuMDAxKQ0Kc3BlZWNoLmRmWywtMV09Zi5zbW9vdGgudG9waWMoeD1zcGVlY2guZGZbLDFdLCB5PXNwZWVjaC5kZlssLTFdKQ0KcGxvdC5zdGFja2VkKHNwZWVjaC5kZlssMV0sIHNwZWVjaC5kZlssdG9waWMucGxvdCsxXSwNCiAgICAgICAgICAgICB4bGFiPSJTZW50ZW5jZXMiLCB5bGFiPSJUb3BpYyBzaGFyZSIsIG1haW49Ikdlb3JnZSBCdXNoLCBpbmF1Z3VyYWwgU3BlZWNoZXMiKQ0KDQpzcGVlY2guZGY9dGJsX2RmKGNvcnB1cy5saXN0LmRmKSU+JWZpbHRlcihGaWxlPT0iV2lsbGlhbUpDbGludG9uIiwgdHlwZT09ImluYXVnIiwgVGVybT09MSklPiVzZWxlY3Qoc2VudC5pZCwgRWNvbm9teTpMZWdpc2xhdGlvbikNCnNwZWVjaC5kZj1hcy5tYXRyaXgoc3BlZWNoLmRmKQ0Kc3BlZWNoLmRmWywtMV09cmVwbGFjZShzcGVlY2guZGZbLC0xXSwgc3BlZWNoLmRmWywtMV08MS8xNSwgMC4wMDEpDQpzcGVlY2guZGZbLC0xXT1mLnNtb290aC50b3BpYyh4PXNwZWVjaC5kZlssMV0sIHk9c3BlZWNoLmRmWywtMV0pDQpwbG90LnN0YWNrZWQoc3BlZWNoLmRmWywxXSwgc3BlZWNoLmRmWyx0b3BpYy5wbG90KzFdLA0KICAgICAgICAgICAgIHhsYWI9IlNlbnRlbmNlcyIsIHlsYWI9IlRvcGljIHNoYXJlIiwgbWFpbj0iV2lsbGlhbSBKIENsaW50b24sIGluYXVndXJhbCBTcGVlY2hlcyIpDQoNCnNwZWVjaC5kZj10YmxfZGYoY29ycHVzLmxpc3QuZGYpJT4lZmlsdGVyKEZpbGU9PSJHZW9yZ2VXQnVzaCIsIHR5cGU9PSJpbmF1ZyIsIFRlcm09PTEpJT4lc2VsZWN0KHNlbnQuaWQsIEVjb25vbXk6TGVnaXNsYXRpb24pDQpzcGVlY2guZGY9YXMubWF0cml4KHNwZWVjaC5kZikNCnNwZWVjaC5kZlssLTFdPXJlcGxhY2Uoc3BlZWNoLmRmWywtMV0sIHNwZWVjaC5kZlssLTFdPDEvMTUsIDAuMDAxKQ0Kc3BlZWNoLmRmWywtMV09Zi5zbW9vdGgudG9waWMoeD1zcGVlY2guZGZbLDFdLCB5PXNwZWVjaC5kZlssLTFdKQ0KcGxvdC5zdGFja2VkKHNwZWVjaC5kZlssMV0sIHNwZWVjaC5kZlssdG9waWMucGxvdCsxXSwNCiAgICAgICAgICAgICB4bGFiPSJTZW50ZW5jZXMiLCB5bGFiPSJUb3BpYyBzaGFyZSIsIG1haW49Ikdlb3JnZSBXLiBCdXNoLCBpbmF1Z3VyYWwgU3BlZWNoZXMiKQ0KDQpzcGVlY2guZGY9dGJsX2RmKGNvcnB1cy5saXN0LmRmKSU+JWZpbHRlcihGaWxlPT0iQmFyYWNrT2JhbWEiLCB0eXBlPT0iaW5hdWciLCBUZXJtPT0xKSU+JXNlbGVjdChzZW50LmlkLCBFY29ub215OkxlZ2lzbGF0aW9uKQ0Kc3BlZWNoLmRmPWFzLm1hdHJpeChzcGVlY2guZGYpDQpzcGVlY2guZGZbLC0xXT1yZXBsYWNlKHNwZWVjaC5kZlssLTFdLCBzcGVlY2guZGZbLC0xXTwxLzE1LCAwLjAwMSkNCnNwZWVjaC5kZlssLTFdPWYuc21vb3RoLnRvcGljKHg9c3BlZWNoLmRmWywxXSwgeT1zcGVlY2guZGZbLC0xXSkNCnBsb3Quc3RhY2tlZChzcGVlY2guZGZbLDFdLCBzcGVlY2guZGZbLHRvcGljLnBsb3QrMV0sDQogICAgICAgICAgICAgeGxhYj0iU2VudGVuY2VzIiwgeWxhYj0iVG9waWMgc2hhcmUiLCBtYWluPSJCYXJhY2sgT2JhbWEsIGluYXVndXJhbCBTcGVlY2hlcyIpDQpgYGANCmBgYHtyLCBmaWcud2lkdGg9NiwgZmlnLmhlaWdodD0xMH0NCiMgWzFdICJFY29ub215IiAgICAgICAgICJBbWVyaWNhIiAgICAgICAgICJEZWZlbnNlIiAgICAgICAgICJCZWxpZWYiICAgICAgICAgDQojIFs1XSAiRWxlY3Rpb24iICAgICAgICAiUGF0cmlvdGlzbSIgICAgICAiVW5pdHkiICAgICAgICAgICAiR292ZXJubWVudCIgICAgIA0KIyBbOV0gIlJlZm9ybSIgICAgICAgICAgIlRlbXBvcmFsIiAgICAgICAgIldvcmtpbmdGYW1pbGllcyIgIkZyZWVkb20iICAgICAgICANCiMgWzEzXSAiRXF1YWxpdHkiICAgICAgICAiTWlzYyIgICAgICAgICAgICAiTGVnaXNsYXRpb24iICAgICAgIA0KDQoNCnBhcihtZnJvdz1jKDUsIDEpKQ0KDQp0b3BpYy5wbG90PWMoMSwgMTMsIDE0LCAxNSwgOCwgOSwgMTIpDQpwcmludCh0b3BpY3MuaGFzaFt0b3BpYy5wbG90XSkNCg0Kc3BlZWNoLmRmPXRibF9kZihjb3JwdXMubGlzdC5kZiklPiVmaWx0ZXIoRmlsZT09IlJvbmFsZFJlYWdhbiIsIHR5cGU9PSJmYXJld2VsbCIpJT4lc2VsZWN0KHNlbnQuaWQsIEVjb25vbXk6TGVnaXNsYXRpb24pDQpzcGVlY2guZGY9YXMubWF0cml4KHNwZWVjaC5kZikNCnNwZWVjaC5kZlssLTFdPXJlcGxhY2Uoc3BlZWNoLmRmWywtMV0sIHNwZWVjaC5kZlssLTFdPDEvMTUsIDAuMDAxKQ0Kc3BlZWNoLmRmWywtMV09Zi5zbW9vdGgudG9waWMoeD1zcGVlY2guZGZbLDFdLCB5PXNwZWVjaC5kZlssLTFdKQ0KcGxvdC5zdGFja2VkKHNwZWVjaC5kZlssMV0sIHNwZWVjaC5kZlssdG9waWMucGxvdCsxXSwNCiAgICAgICAgICAgICB4bGFiPSJTZW50ZW5jZXMiLCB5bGFiPSJUb3BpYyBzaGFyZSIsIG1haW49IlJvbmFsZCBSZWFnYW4sIEZhcmV3ZWxsIFNwZWVjaGVzIikNCg0Kc3BlZWNoLmRmPXRibF9kZihjb3JwdXMubGlzdC5kZiklPiVmaWx0ZXIoRmlsZT09Ikdlb3JnZUJ1c2giLCB0eXBlPT0iZmFyZXdlbGwiKSU+JXNlbGVjdChzZW50LmlkLCBFY29ub215OkxlZ2lzbGF0aW9uKQ0Kc3BlZWNoLmRmPWFzLm1hdHJpeChzcGVlY2guZGYpDQpzcGVlY2guZGZbLC0xXT1yZXBsYWNlKHNwZWVjaC5kZlssLTFdLCBzcGVlY2guZGZbLC0xXTwxLzE1LCAwLjAwMSkNCnNwZWVjaC5kZlssLTFdPWYuc21vb3RoLnRvcGljKHg9c3BlZWNoLmRmWywxXSwgeT1zcGVlY2guZGZbLC0xXSkNCnBsb3Quc3RhY2tlZChzcGVlY2guZGZbLDFdLCBzcGVlY2guZGZbLHRvcGljLnBsb3QrMV0sDQogICAgICAgICAgICAgeGxhYj0iU2VudGVuY2VzIiwgeWxhYj0iVG9waWMgc2hhcmUiLCBtYWluPSJHZW9yZ2UgQnVzaCwgRmFyZXdlbGwgU3BlZWNoZXMiKQ0KDQpzcGVlY2guZGY9dGJsX2RmKGNvcnB1cy5saXN0LmRmKSU+JWZpbHRlcihGaWxlPT0iV2lsbGlhbUpDbGludG9uIiwgdHlwZT09ImZhcmV3ZWxsIiklPiVzZWxlY3Qoc2VudC5pZCwgRWNvbm9teTpMZWdpc2xhdGlvbikNCnNwZWVjaC5kZj1hcy5tYXRyaXgoc3BlZWNoLmRmKQ0Kc3BlZWNoLmRmWywtMV09cmVwbGFjZShzcGVlY2guZGZbLC0xXSwgc3BlZWNoLmRmWywtMV08MS8xNSwgMC4wMDEpDQpzcGVlY2guZGZbLC0xXT1mLnNtb290aC50b3BpYyh4PXNwZWVjaC5kZlssMV0sIHk9c3BlZWNoLmRmWywtMV0pDQpwbG90LnN0YWNrZWQoc3BlZWNoLmRmWywxXSwgc3BlZWNoLmRmWyx0b3BpYy5wbG90KzFdLA0KICAgICAgICAgICAgIHhsYWI9IlNlbnRlbmNlcyIsIHlsYWI9IlRvcGljIHNoYXJlIiwgbWFpbj0iV2lsbGlhbSBKLiBDbGludG9uLCBGYXJld2VsbCBTcGVlY2hlcyIpDQoNCnNwZWVjaC5kZj10YmxfZGYoY29ycHVzLmxpc3QuZGYpJT4lZmlsdGVyKEZpbGU9PSJHZW9yZ2VXQnVzaCIsIHR5cGU9PSJmYXJld2VsbCIpJT4lc2VsZWN0KHNlbnQuaWQsIEVjb25vbXk6TGVnaXNsYXRpb24pDQpzcGVlY2guZGY9YXMubWF0cml4KHNwZWVjaC5kZikNCnNwZWVjaC5kZlssLTFdPXJlcGxhY2Uoc3BlZWNoLmRmWywtMV0sIHNwZWVjaC5kZlssLTFdPDEvMTUsIDAuMDAxKQ0Kc3BlZWNoLmRmWywtMV09Zi5zbW9vdGgudG9waWMoeD1zcGVlY2guZGZbLDFdLCB5PXNwZWVjaC5kZlssLTFdKQ0KcGxvdC5zdGFja2VkKHNwZWVjaC5kZlssMV0sIHNwZWVjaC5kZlssdG9waWMucGxvdCsxXSwNCiAgICAgICAgICAgICB4bGFiPSJTZW50ZW5jZXMiLCB5bGFiPSJUb3BpYyBzaGFyZSIsIG1haW49Ikdlb3JnZSBXIEJ1c2gsIEZhcmV3ZWxsIFNwZWVjaGVzIikNCg0KDQpzcGVlY2guZGY9dGJsX2RmKGNvcnB1cy5saXN0LmRmKSU+JWZpbHRlcihGaWxlPT0iQmFyYWNrT2JhbWEiLCB0eXBlPT0iZmFyZXdlbGwiKSU+JXNlbGVjdChzZW50LmlkLCBFY29ub215OkxlZ2lzbGF0aW9uKQ0Kc3BlZWNoLmRmPWFzLm1hdHJpeChzcGVlY2guZGYpDQpzcGVlY2guZGZbLC0xXT1yZXBsYWNlKHNwZWVjaC5kZlssLTFdLCBzcGVlY2guZGZbLC0xXTwxLzE1LCAwLjAwMSkNCnNwZWVjaC5kZlssLTFdPWYuc21vb3RoLnRvcGljKHg9c3BlZWNoLmRmWywxXSwgeT1zcGVlY2guZGZbLC0xXSkNCnBsb3Quc3RhY2tlZChzcGVlY2guZGZbLDFdLCBzcGVlY2guZGZbLHRvcGljLnBsb3QrMV0sDQogICAgICAgICAgICAgeGxhYj0iU2VudGVuY2VzIiwgeWxhYj0iVG9waWMgc2hhcmUiLCBtYWluPSJCYXJhY2sgT2JhbWEsIEZhcmV3ZWxsIFNwZWVjaGVzIikNCmBgYA0KDQpgYGB7cn0NCnNwZWVjaC5kZj10YmxfZGYoY29ycHVzLmxpc3QuZGYpJT4lZmlsdGVyKHR5cGU9PSJub21pbiIsIHdvcmQuY291bnQ8MjApJT4lc2VsZWN0KHNlbnRlbmNlcywgRWNvbm9teTpMZWdpc2xhdGlvbikNCg0KYXMuY2hhcmFjdGVyKHNwZWVjaC5kZiRzZW50ZW5jZXNbYXBwbHkoYXMuZGF0YS5mcmFtZShzcGVlY2guZGZbLC0xXSksIDIsIHdoaWNoLm1heCldKQ0KDQpuYW1lcyhzcGVlY2guZGYpWy0xXQ0KDQpgYGANCg0KDQpgYGB7ciwgZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQ9Nn0NCnByZXNpZC5zdW1tYXJ5PXRibF9kZihjb3JwdXMubGlzdC5kZiklPiUNCiAgZmlsdGVyKHR5cGU9PSJpbmF1ZyIsIEZpbGUlaW4lc2VsLmNvbXBhcmlzb24pJT4lDQogIHNlbGVjdChGaWxlLCBFY29ub215OkxlZ2lzbGF0aW9uKSU+JQ0KICBncm91cF9ieShGaWxlKSU+JQ0KICBzdW1tYXJpc2VfZWFjaChmdW5zKG1lYW4pKQ0KDQpwcmVzaWQuc3VtbWFyeT1hcy5kYXRhLmZyYW1lKHByZXNpZC5zdW1tYXJ5KQ0Kcm93bmFtZXMocHJlc2lkLnN1bW1hcnkpPWFzLmNoYXJhY3RlcigocHJlc2lkLnN1bW1hcnlbLDFdKSkNCmttLnJlcz1rbWVhbnMoc2NhbGUocHJlc2lkLnN1bW1hcnlbLC0xXSksIGl0ZXIubWF4PTIwMCwNCiAgICAgICAgICAgICAgNSkNCmZ2aXpfY2x1c3RlcihrbS5yZXMsIA0KICAgICAgICAgICAgIHN0YW5kPVQsIHJlcGVsPSBUUlVFLA0KICAgICAgICAgICAgIGRhdGEgPSBwcmVzaWQuc3VtbWFyeVssLTFdLA0KICAgICAgICAgICAgIHNob3cuY2x1c3QuY2VudD1GQUxTRSkNCmBgYA0KDQo=